こんにちは!リンコですピヨ!この記事では自分の勉強の復習もかねて、Kotlinでのnull許容型の変数について解説するピヨ!
この第25章では、プログラミング言語であるKotlinのnull許容型の変数について、一緒に勉強していきましょう。
この記事を読めばプログラミング未経験の方も、Kotlin入門レベルのnull許容型の変数について1つ1つ理解しながら勉強できると思うので、是非最後まで読んで頂ければと思います。
※この記事で出てくる「サンプルコード」は、記述が長く画面からはみ出ている場合がありますが、横にスライドすると表示されるのでご安心ください。
【Kotlin】例外(NullPointerException)についてのおさらい
アプリやゲームが落ちてしまったりフリーズしてしてしまうのは、Kotlinのプログラム内でいろいろな「例外」と呼ばれるエラーが発生しているということは、前章で勉強していきました。
特に「NullPointerException」と呼ばれる例外は、高頻度で発生してアプリの品質を下げてしまう原因になることが多いです。
現時点ではAndroidアプリはKotlinよりもJavaで作られていることの方が多いのですが、Javaの仕様上この例外が起きるコードを気づかずに書いてしまいやすいのです。
ですがKotlinでは、言語の仕様上「NullPointerException」の例外を防げるような仕組みが用意されていて、「実行時」まで問題が持ち越されるのを未然に防ぐために、文法上の仕様で「コンパイル時」に問題を発見できるようになっています。
以下のコンパイルエラーが起きるサンプルコードを見てみましょう。
var name: String = "リンコ" name = null //ここでコンパイルエラーが起きます println("${name}の文字数は" + name.length + "文字です")
もちろん3行目でもコンパイルエラーが起きますが、上記を実行するとその前に、2行目でコンパイルエラーになり実行できません。
Javaなどの言語では、2行目の変数にnullを代入するコードはエラーとして認識されませんが、Kotlinではエラーとして認識されます。
このようにKotlinでは、文法上の決まりをJavaよりもある程度厳しくすることにより、潜在的に問題が起きやすいコードを記述できないような仕組みになっています。
実行すらできないようになっているので、気づかずアプリをリリースしてユーザーをガッカリさせてしまうようなこともなくなります。
例外の名前の一部になっているnull(ヌル)というのが、Kotlinの特別なキーワードで「何もない」という状態を表しているピヨ!
【Kotlin】null許容型の変数とは?
では本題の、null許容型の変数とはいったいなんなんでしょうか?null許容型の変数について勉強していきましょう。
意図的に何もない状態を作ること
上記で説明したように、変数にnullをセットできないようにして例外が起きないように安全を保つのがKotlinですが、絶対に変数にnullを代入できないというわけではありません。
プログラムを記述していると、場合によっては変数を「何もない」という状態にしておいた方が便利な時もあります。
そんな時は、意図的にnullを使うのですが、nullを変数に代入できるようにすることを「null許容型の変数」と言います。
基本的にKotlinの型は非null型
Kotlinの型は、String型やInt型といったどの型も基本的にnullを代入できない非null型です。
nullという値が代入されるのを禁止することにより、予期せずnullが代入されてしまったコードをコンパイル時にチェックし、実行時のエラーを回避しています。
ですがこのnull許容型にすることによって、nullを代入することもできるようになります。
基本的な型は非null型で、型名の後ろに「?」を付けて宣言するとnull許容型になるピヨ!
【Kotlin】null許容型の変数を使ってみよう
では実際にnull許容型の変数を使ってみましょう。
null許容型の書式
null許容型の書式は以下のようになります。
var 変数名: String? = "値"
このように「String」の後ろに「?」を付けることによって、nullを代入することが可能な変数として宣言しています。型の後ろに「?」を使って宣言された変数を「null許容型の変数」と言います。
実際にnull許容型の変数を記述してみよう
では実際にコードを記述してみましょう。
サンプルコード
以下のコードは、先程のサンプルコードの型に「?」をつけて実行しています。
var name: String? = "リンコ" name = null //コンパイルエラーが起きなくなりました println("${name}の文字数は" + name?.length + "文字です")
出力
サンプルコードを実行すると、以下のように出力されます。
nullの文字数はnull文字です
先程のサンプルコードを実行した際は2行目でコンパイルエラーになっていましたが、今回はコンパイルが正常に行われ、2行目も実行されました。
これは1行目の型宣言の際に「String?」として、nameという変数を「null許容型の変数」にしたからです。
また今回のコードでは、最後の3行目までエラーが起こることなく正常に実行されました。Javaであれば、3行目の実行時に変数の中身がnullだと「NullPointerException」が発生しますが、Kotlinのコードでは、例外が起こることなく「nullの文字数はnull文字です」と出力されました。
「name」も「name?.length」もnullと出力されていますね。変数単体であればそのままでいいのですが「name?.length」のように、nullの可能性があってlengthプロパティを参照する際は、変数の後ろに「?」を付ける必要があります。
これは「null許容型の変数がnullじゃなかったら通常通り文字数を返し、nullの場合はnullを返す」というような意味になります。
このようにnull許容型の変数を使うことで、実行時に「NullPointerException」が発生することがなくなります。
ちなみに変数の後に「?.」を付けてプロパティなどを参照する方法を「セーフコール演算子」と呼びます。
値がnullじゃない時だけ実行される「セーフコール演算子」についても覚えておくピヨ!
【Kotlin】null許容型は取り扱いに注意
null許容型には、当然nullが入っている可能性があるので、取り扱いには注意が必要です。特にこれから紹介する3つのパターンには注意しましょう。
null許容型がプロパティなどを参照する際は「?.」を使う
まず1つ目に、先程軽く触れましたが、null許容型の変数がプロパティなどを参照する際には「?.(セーフコール演算子)」を使います。
以下のコードを見てみましょう。
サンプルコード
var name1: String? = "リンコ" println(name1?.length) var name2: String? = "リンコ" println(name2.length)
出力
3 Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
このように、1つ目のコードだとnull許容型の変数に対して「セーフコール演算子」を使っているのでエラーになりません。
ですが2つ目のコードのように、null許容型の変数に対して「セーフコール演算子」なしにプロパティを参照しようとすると、エラーが出力されていますので注意しましょう。
またnullの場合に決まった値を出力したい時は「?:(エルビス演算子)」を使いましょう。
サンプルコード
var name3: String? = null println(name3?.length ?: 0)
出力
0
上記の場合はエルビス演算子を使い、値がnullだった時に「0」を出力するように記述しています。
実際に値が0だったので、0と出力されましたね。
もう1つ、null許容型の変数を非null型の変数に変換したい場合には「!!演算子」を使います。
サンプルコード
var name4: String? = "リンコ" println(name4!!.length)
出力
3
本来であればnull許容型の変数は、セーフコール演算子を使ってプロパティを参照しないとエラーが起こりますが、上記のコードでは「!!演算子」を使ってnull許容型の変数を非null型の変数に変換しているので、エラーが出力されませんでした。
ただし、上記のサンプルコードで言うと、変数name4の値がnullだった場合に「NullPointerException」が発生してしまうので、null許容型の変数の値がnullではないことが保証されている場合にのみ「!!演算子」は使うようにしましょう。
「!!演算子」は特に取り扱いに注意ピヨ!
非null型をnull許容型に代入する際のボクシングに注意
続いて2つ目に、非null型をnull許容型に代入すると、暗黙的に型変換されてしまうので注意しましょう。
この暗黙的な型変換のことを「ボクシング」と言います。ではどういうものなのか実際に勉強していきます。
サンプルコード
var num1: Int = 1000 var num2: Int = num1 var num3: Int? = num1 println(num1 == num2) println(num1 === num2) println(num1 == num3) println(num1 === num3)
上記のコードでは、数値の1000が入った変数num1を、非null型の変数num2とnull許容型の変数num3にそれぞれ代入しています。
ここで「==」と「===」の記号が出てきましたが、同値性の比較には「==」、同一性の比較には「===」の演算子を使います。
同じ値を持つことを「同値」、同じオブジェクトであることを「同一」と言うピヨ!
出力
true true true false
変数num1と変数num2の比較結果はどちらも「true」になっているので、同値であり同一という結果になりました。
対して変数num1と変数num3の比較結果は片方が「false」になっており、同値ですが同一ではないという結果になりました。
これにより変数num3がnull許容型の変数なのが原因で、暗黙的な型変換「ボクシング」が発生したことにより、別のオブジェクトになったことがわかります。
null許容型は非null型に代入できない
最後に3つ目ですが、null許容型の変数は非null型の変数に代入できないということです。
以下のコードを見てみましょう。
サンプルコード
var name1: String? = "リンコ" var name2: String = name1
出力
Type mismatch: inferred type is String? but String was expected
上記のサンプルコードでは、「リンコ」と言う文字列の入ったnull許容型の変数name1を、非null型の変数name2に代入しようとしています。
ですが上手く代入されず、エラーが出力されてしまいました。このように、null許容型の変数は非null型の変数に代入できないので注意しましょう。
まとめ
この記事では、プログラミング言語であるKotlinのnull許容型の変数について勉強していきましたが、いかがでしたでしょうか?今回の記事をまとめると以下のようになります。
- Kotlinでは例外(NullPointerException)が起きにくい仕組みになっている
- 意図的に何もない状態の変数を作ることをnull許容型と言う
- null許容型は宣言時の型の後ろに「?」をつける
- 変数の後に「?.」を付けてプロパティなどを参照する方法を「セーフコール演算子」と言う
- 暗黙的な型変換のことを「ボクシング」と言う
- null許容型は非null型に代入できないので注意
今回勉強した「null許容型の変数」はKotlinの入門レベルの知識になるので、何回も読み直してしっかり覚えておきましょう。
次回の記事では「Anyクラスと3つの関数」について勉強していくピヨ!
プログラミング未経験の方や入門レベルの方、Kotlinについて詳しくなりたい方は、また一緒に勉強するピヨ!
コメント