Kotlinマイルストーン9がリリースされた!
KotlinのNull-safetyは死んでしまったのかい?
— たろう (@ngsw_taro) 2014, 10月 16
これについて。
Javaのメソッドの戻り値の型がPlatform Typeって呼ばれる特殊な感じになったらしい。
今まではJavaのメソッドの戻り値の型は@NotNullなどで明示していない限り、すべてNullableとして扱われていた。
// Java public static Integer id(final Integer value) { return value; }
// M9より前 val a = id(5) // aの型はInt? println(a.plus(2)) // コンパイルエラー
M9では上記の変数aの型がPlatform Typeとなってこうなる。
// M9 val a = id(5) // aの型はInt! println(a.plus(2)) // => 7
このコードはコンパイル可能で、実行時にもエラーは起こらない。
コンパイルがうまく行くのはaがPlatform Typeとして扱われるからで、実行時にエラーが起こらないのはaがnullでないから。Platform TypeのIntなのでaはInt!型となる、らしい。
では、nullの場合はどうなるのか。
val b = id(null) // bの型はInt! println(b.plus(2)) // ここで例外
先ほどの例と同様にコンパイルは通る。しかし実行するとb.plus(2)の箇所で例外を投げる。
Exception in thread "main" java.lang.NullPointerException
at sample.SamplePackage$Main$6051fa33.main(Main.kt:8)
at sample.SamplePackage.main(Main.kt:1)
・・・
ぬるぽ怖い。なので型を明示する。
val b: Int? = id(null) // bの型はInt? println(b.plus(2)) // コンパイルエラー
型を明示することでPlatform Type(Int!)を使わずにNullable(Int?)として変数を扱えるようになる。
逆にNotNullを表明することもできる。
val a: Int = id(5) // aの型はInt println(a.plus(2)) // => 7
さらにnullが返ってくるにも関わらずNotNull型として宣言できちゃう。
val b: Int = id(null) // ここでNullPointerException println(b.plus(2))
コンパイルは通る。bはInt型として扱われるのでb.plus(2)も問題なくコンパイルされる。
しかし今回の場合bはnullなのでval b: Int = id(null)のタイミングでNPEが投げられる。コンパイルして生成されたバイトコードを見るとNullチェックが差し込まれているのがわかる*1。
このPlatform TypeによりJavaコードの呼び出しが手軽になった。けれど簡単にNullPointerExceptionが起こるようになってしまったと思う。。*2