Kotlinは NullPointerException(NPE)をできるだけ防ぐ設計を持つプログラミング言語です。
Javaでは null によるエラーが頻発しますが、Kotlinでは 型システムでnullを管理する仕組みが組み込まれています。
つまり、Kotlinでは
- nullを許可する型
- nullを許可しない型
が明確に区別されています。
Kotlinのnull安全の基本
Kotlinでは型により null 許容が決まります。
val a: String = "Hello"
この型は nullを許可しません
a = null // コンパイルエラー
一方、次のように ? を付けると null を許可します。
val b: String? = "Hello"
この場合
val b: String? = null
も可能です。
つまり基本ルールは次です。
| 型 | null |
|---|---|
String | 許可しない |
String? | 許可する |
nullable型はそのままメソッドを呼べない
nullable型では、直接メンバーアクセスできません。
val name: String? = "Taro"
println(name.length)
これは コンパイルエラーになります。
なぜなら、name が null の可能性があるからです。
この問題を解決するための仕組みが safe call です。
Safe Call(安全呼び出し)?.
safe call は次のように書きます。
name?.length
動作は次の通りです。
name != null → lengthを取得
name == null → 式の結果はnull
例
val name: String? = null
println(name?.length)
name?.length の評価結果は null になり、それが println に渡されるため null と表示されます。
重要なポイントは
safe call は null を返す
ということです。
Elvis演算子 ?:
nullの場合の代替値を指定する方法です。
val name: String? = null
val length = name?.length ?: 0
println(length)
意味
name?.length が nullなら 0 を返す
つまり
A ?: B
は
AがnullならB
という意味です。
この演算子はKotlinコードで非常によく使われます。
非nullアサーション !!
!! は 強制的に非nullとして扱う演算子です。
val name: String? = "Taro"
println(name!!.length)
もし name が null の場合
NullPointerException
が発生します。
つまり
nullable型を強制的にnon-nullとして扱う
ための演算子です。
ただし、この方法は Kotlinのnull安全を無効化するため、実務では なるべく使わない のが基本です。
ifによるnullチェックとSmart Cast
Kotlinではnullチェックをすると、コンパイラが型を推論します。
val name: String? = "Taro"
if (name != null) {
println(name.length)
}
このブロック内では
name は String として扱われる
これを Smart Cast と呼びます。
ただし注意点があります。
Smart Cast は次のような場合に成立します。
- ローカル変数
- 再代入されない
val - コンパイラが値が変わらないと判断できる場合
mutableなプロパティでは成立しないことがあります。
?.let を使ったnullチェック
Kotlinでは次の書き方がよく使われます。
name?.let {
println(it.length)
}
これは
nameがnullでない場合だけ実行
という意味です。
内部的には次のような処理に近いです。
if (name != null) {
println(name.length)
}
ここで重要なのは
let がnullチェックしているのではない
という点です。
nullを回避しているのは
?. (safe call)
です。
Safe Callの連鎖
safe call はチェーンできます。
user?.address?.city
この処理は次のような意味になります。
userがnullなら終了
addressがnullなら終了
cityを取得
Javaで書くと次のようになります。
if(user != null){
if(user.getAddress() != null){
return user.getAddress().getCity();
}
}
Kotlinでは非常に簡潔に書けます。
また、safe callを使った式の結果は通常 nullable型になります。
val city: String? = user?.address?.city
requireNotNull と checkNotNull
Kotlinには null をチェックする標準関数があります。
requireNotNull
val name: String? = "Taro"
val result = requireNotNull(name)
println(result.length)
nullの場合
IllegalArgumentException
が発生します。
主な用途は
引数のバリデーション
です。
checkNotNull
val config: String? = "value"
val result = checkNotNull(config)
nullの場合
IllegalStateException
になります。
主な用途
オブジェクトの状態チェック
KotlinでもNullPointerExceptionは起こり得る
Kotlinはnull安全な言語ですが、NPEが完全に無くなるわけではありません。
主な原因は次の通りです。
!! を使用した場合
name!!.length
Javaコードとの相互運用
Javaにはnull許容情報がない場合があるため
platform type
として扱われます。
lateinit未初期化
lateinit var name: String
未初期化のまま使うと
UninitializedPropertyAccessException
が発生します。
外部ライブラリ
Javaライブラリからnullが渡されることがあります。
コレクションとnull
Kotlinではコレクションでもnullの扱いが分かれます。
List<String>
リストも要素もnull不可
List<String?>
要素がnull可能
List<String>?
リスト自体がnull可能
List<String?>?
両方null可能
この違いは実務で重要です。
実務でよく使うnull処理パターン
Safe Call
user?.name
Elvis演算子
val name = user?.name ?: "guest"
let
user?.let {
println(it.name)
}
ifチェック
if (user != null) {
println(user.name)
}
まとめ
Kotlinのnull安全の基本は次の通りです。
型でnullを管理
String
String?
safe callでnull回避
?.
Elvis演算子で代替値
?:
smart cast
if(x != null)
!! は最後の手段
Kotlinのnull安全は単なる文法ではなく、言語設計の中心的思想です。
これを理解すると、Javaよりも 安全で読みやすいコードを書くことができます。
以上、Kotlinのnullチェックについてでした。
最後までお読みいただき、ありがとうございました。










