kotlinで本物のヌルポを出す方法

  • 33
    Like
  • 0
    Comment

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 の取扱には気をつけましょう。