Kotlin の特徴の一つが Null安全 という考え方です。
ここでは、Kotlin における Null安全の仕組みについてまとめます。
参考
Kotlin の特徴については、「Kotlinとは?」にまとめています。
JavaにおけるNullの扱い
Java では、以下のようなコードを書くと NullPointerException の例外が発生します。
|
1 2 3 |
String s = null; s.toUpperCase(); // NullPointerException 発生 |
おそらく、Java のコードを書いたことがある方は、この例外と仲良しなはずです(笑)
馴染みの深い例外ですが、なかなか簡単には排除できないことも確かです。
排除できない理由としては、以下のことが考えられます。
- Null となる参照と、そうでない参照の区別がない(すべてのオブジェクトが Null になりえる)
- Null となりえることを、プログラマがテストしない
Null かどうかを区別する方法は、いくつかあります。
例えば、@Nonnull とか @Nullable というアノテーションを使って、静的解析ツールで防ぐ方法です。
しかし、完全ではありませあん。おそらく、この方法では Null 問題を解消することはできません。
そこで Kotlin に登場したのが、「Null安全」という考え方です。
KotlinのNull安全(Null Safety)
Kotlin にも Null は存在します。
Java との違いは、「Null となりえるか否かを明確に区別する」という考え方・文法です。
Null許容型の宣言
例えば、以下のようなコードを書くと、コンパイルエラーになります。
|
1 2 |
var s: String = null |
コンパイル結果
Null can not be a value of a non-null type String
「Null は 非Null の String の値になることはできない」というエラーです。
null を代入するには、「?」記号を型の後に付ける必要があります。
|
1 2 |
var s: String? = null |
Null安全呼出し
Null 許容型で宣言した String の変数に null を代入して呼び出すとどうなるのでしょうか?
|
1 2 3 |
var s: String? = null s.toUpperCase() |
コンパイル結果
Only safe (?.) or non-null asserted (!!.) calls are allowed
on a nullable receiver of type String?
コンパイルに失敗します。
Null になる可能性のある参照対して、メソッドやプロパティのアクセスはできません。
たとえ実際に "Kotlin" という文字列が代入されていてもコンパイルエラーとなります。
以下のように Null チェックしてアクセスすることでコンパイルエラーを回避できます。
|
1 2 3 4 5 |
var s: String? = null if (s != null) { s.toUpperCase() } |
Null チェック済みの if ブロック内では、Null 非許容型に自動的にキャストされ、
メソッドを呼び出すことができます。
これを、「スマートキャスト」 と言います。
また、Kotlin では安全呼出し(safe call)と呼ばれる構文糖衣があります。
|
1 2 3 |
var s: String? = null s?.toUpperCase() |
?. に続けて メソッドの呼出しやプロパティにアクセスできます。
上記の例だと、変数 s が null だった場合は、ただの null が返されます。
!! 演算子
Nullable(Null許容型)を NotNull(Null非許容型)に強制的に変換する演算子が用意されています。
「!!」記号を参照の後に付けることで、NotNull 型に変換されます。
|
1 2 3 4 |
var s: String? = null var t: String = s!! s.toUpperCase() |
!! 演算子を付けると、コンパイルエラーにはなりません。
実行時に、 NullPointerException の例外が発生します。
この変換方法は、結局 Java の実装と変わらなくなるため、乱用はしない方がいいでしょう。
どうしても型変換をしたい場合は、標準ライブラリ関数の requireNotNull を使いましょう。
|
1 2 3 4 |
var s: String? = null var t: String = requireNotNull(s, {"sはNULLではダメ!"}) s.toUpperCase() |
実行結果
Exception IllegalArgumentException: sはNULLではダメ!