はじめに
こんにちは。今回は改めてKotlinでのnullの扱いについての基本をまとめます。
曖昧な理解のままAndroidStudioの補完に頼りきりでしたので、基本的な書き方を網羅することで、Kotlinでどのようなコードを書けばよいのかという指針になればと思います。
なお、コードを試す際はブラウザからKotlinの動作を確認できるTry Kotlinを使用します。
基本的にnullを許容しない
Javaは全ての型にnullが代入される可能性があります。そのためコードのあちこちで
if (object != null) {...}
というnullチェックを書く必要がありましたが、Kotlinでは変数にnullが入らないこと(non-null)を基本としています。
nullが入り込まないことが前提であれば、NPE(NullPointerException)は起こり得ません。
装飾なしの型宣言ではnullを代入できず、コンパイルエラーとなります。
1 2 | var message: String = "hello!" message = null // コンパイルエラー |
nullを許容するNullable
型名に「?」をつけることでnullを入れられる(Nullable、null許容)ようになりますが、Nullableのままメソッドやプロパティを呼び出そうとするとコンパイルエラーになります。
Javaであれば、実行時にNullPointerExceptionとなることで初めてコードの間違いに気づけますが、Kotlinではコンパイルの時点で指摘してくれます。
1 2 3 | var message: String? = "it's Nullable!" message = null println(message.length) // コンパイルエラー |
また、non-nullの変数にNullableを代入することもできません。
String
と
String?
は異なる型として扱われるためです。
下記のコードの場合はたまたま
message1
に文字列が入っていますが、nullになりうる型のままでは、non-nullである
message2
への代入は許されないということですね。
1 2 | var message1: String? = "Is this null?" var message2: String = message1 // コンパイルエラー |
Nullableをnon-nullに変える
いくつかの方法があります。
nullチェックとスマートキャスト
if文でnullが入っていないことを確認できた場合、その変数はifのブロック内でのみnon-nullとして扱えます。
この動作はKotlinのスマートキャスト機能によるものです。
1 2 3 4 5 6 | var message1: String? = "this is Nullable?" if (message != null) { println(message.length) // nullではないことが確認できているので、コンパイルおよび実行可 var message2: String = message1 // message1がNullableからnon-nullに変わっているので代入可 } var message3: String = message1 // ifブロックの外に出るとコンパイルエラー |
なおスマートキャストはnullチェックだけでなく、変数の型チェック全般で使えるものです。
以下の例では、Any型として初期化されている変数の型チェックを行うことで、Int型として加算処理が可能になっています。
1 2 3 4 5 | var age: Any = 19 if (age is Int) { age += 1 // Any型からInt型にスマートキャストされているので加算できる println(age) } |
エルビス演算子
変数への代入に
?:
を使うことで、この演算子の左式がnullだった場合は右式が評価されます。Javaや他言語の三項演算子のように使える感じですね。
1 2 | var userName: String = System.getProperty("user.name").toString() ?: "unknown property." println(userName.length) |
上記コードではJavaのSystem.getProperty()の結果を代入しています。Javaはnull安全性が無いため、Javaライブラリから呼び出した結果は全てNullableとして扱われます。
環境変数user.nameが設定されていないとnullが返ってくるため、その時は
?:
の右式が評価され、変数userNameには
unknown property.
が入ります。
セーフコール演算子
変数のあとに
?.
をつけることで「変数がnullでなければメンバーにアクセスし、nullならnullを返すだけ」という意味になります。
変数
message1
および
message2
が下記コードでnon-nullに変換されるわけではありませんが、変数の値が間違いなくnullではないと分かっていれば、このような記述が可能です。
1 2 3 4 5 | var message1: String? = "あかさたな" println(message1?.length) // 5 var message2: String? = null println(message2?.length) // null |
!!演算子
変数のあとに
!!
をつけることで強制的にnon-nullに変換できます。ただし、中身がnullだった場合はメソッドやプロパティへのアクセス時にNullPointerExceptionが発生します。
コンパイルの時点でnullの可能性を潰せておらず、Kotlinのnull安全性を活かせていないため、非推奨な方法です。
1 2 | var message: String? = null message!!.length // コンパイルは通るが実行時にNullPointerException発生 |
さいごに
いかがでしたでしょうか。IDEの指摘に頼りきってとりあえずエラーを回避するのではなく、基本的な文法を学ぶことで、作法に則ったコードを書く意識が強くなり「どのように書くべきか」の指針を得られたと感じています。
とにかく動くコードこそ正義というケースもあるかもしれませんが、言語の機能を活かすため、ひいてはコードの品質を上げるためにも、勉強を続けようと思います。