kotlinで本物のヌルポを出す方法
kotlinではnull安全ですからヌルポとは出会えません。でも、kotlinのnull安全機構においてクラッシュするケースがあります。 !!
演算子で明示的にクラッシュケースを書いた場合です。このクラッシュはヌルポでは無いのでしょうか?実はドキュメントには、 ヌルポが欲しければ !!
演算子で得られるよ と書かれています。確かめてみましょう。
fun f(): Int? {
return null
}
fun example1() {
val a = f()
val x = a!! + 3 // ここで例外が飛ぶ
println(x)
}
Exception in thread "main" kotlin.KotlinNullPointerException
一見ヌルポが生まれたように見えます。しかしよく見てください。kotlin.KotlinNullPointerException
と書かれています。これはkotlin由来の偽物のヌルポです。やはり本物のヌルポといえば java.lang.NullPointerException
でしょう。
もちろん、Java連携を使えば本物のヌルポを得ることができます。Javaといえばヌルポですから当然です。では、混じり気の無い純粋なKotlinによって、本物のヌルポを得ることはできるのでしょうか。実は最近その方法を知ったのでご紹介します。
class Alpha {
init {
f()
}
fun f() {
val x: Int = a.length // ここで例外が飛ぶ
}
val a: String = "nya"
}
fun example2() {
Alpha()
}
Exception in thread "main" java.lang.NullPointerException
どうですか、これが純粋なKotlinから産まれた本物のヌルポです。ヌルポが飛ぶというのに、コンパイラエラーも出ないし、 !!
演算子を書くような明示的な記述もありません。非ヌル安全だ。最悪だ。
解説
kotlinのクラス定義において、プロパティの初期化と init
の実行は、上から順に実行されるようです。さっきのコードは、下記のように定義順をひっくり返すとクラッシュしなくなります。
class Alpha {
val a: String = "nya"
init {
f()
}
fun f() {
val x: Int = a.length
}
}
また、コンストラクタはプロパティ初期化の後に実行されるので、この問題は起こりません。
class Alpha {
constructor() {
f()
}
fun f() {
val x: Int = a.length
}
val a: String = "nya"
}
というわけで、 init
の取扱には気をつけましょう。