MeWrite Docs

KotlinNullPointerException

Kotlinでnull安全機能を迂回した際のNullPointerException

概要

Kotlinでnull安全機能を迂回(!!演算子の使用など)した際にnull値にアクセスしようとすると発生するエラーです。Kotlinはnull安全を言語レベルでサポートしていますが、強制的にnullを許容する場合にこのエラーが発生します。

エラーメッセージ

kotlin.KotlinNullPointerException
java.lang.NullPointerException: null cannot be cast to non-null type
Exception in thread "main" kotlin.KotlinNullPointerException

原因

  1. !!演算子の乱用: non-null assertionでnull値をアクセス
  2. Javaとの相互運用: Javaコードからのnull値
  3. lateinitの未初期化: lateinitプロパティを初期化前にアクセス
  4. プラットフォーム型: Javaからの戻り値がnull
  5. キャストエラー: nullをnon-null型にキャスト

解決策

1. !!演算子を避ける

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Bad: !!演算子でクラッシュの可能性
val name: String? = null
val length = name!!.length  // KotlinNullPointerException

// Good: safe call演算子を使用
val name: String? = null
val length = name?.length  // null

// Good: Elvis演算子でデフォルト値
val length = name?.length ?: 0

2. nullチェック

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Bad: nullチェックなしでアクセス
fun processUser(user: User?) {
    println(user!!.name)
}

// Good: nullチェックを行う
fun processUser(user: User?) {
    if (user != null) {
        println(user.name)  // スマートキャスト
    }
}

// Good: let を使用
fun processUser(user: User?) {
    user?.let {
        println(it.name)
    }
}

3. lateinitの安全な使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Bad: 初期化前にアクセス
class MyClass {
    lateinit var name: String

    fun printName() {
        println(name)  // UninitializedPropertyAccessException
    }
}

// Good: 初期化チェック
class MyClass {
    lateinit var name: String

    fun printName() {
        if (::name.isInitialized) {
            println(name)
        } else {
            println("Not initialized")
        }
    }
}

4. Javaとの相互運用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// JavaのAPIを呼び出す場合
// Javaからの戻り値はプラットフォーム型(String!)

// Bad: 直接アクセス
val result = javaObject.getValue()  // String!
val length = result.length  // nullの可能性

// Good: nullable型として扱う
val result: String? = javaObject.getValue()
val length = result?.length ?: 0

5. コレクションのnull処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Bad: firstOrNull()の結果に!!
val items = emptyList<String>()
val first = items.firstOrNull()!!  // NPE

// Good: Elvis演算子
val first = items.firstOrNull() ?: "default"

// Good: firstOrNullとlet
items.firstOrNull()?.let { first ->
    println(first)
}

6. マップのアクセス

1
2
3
4
5
6
7
8
9
// Bad: マップに存在しないキーに!!
val map = mapOf("a" to 1)
val value = map["b"]!!  // NPE

// Good: デフォルト値
val value = map["b"] ?: 0

// Good: getOrDefault
val value = map.getOrDefault("b", 0)

7. require/checkNotNull

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 引数のバリデーション
fun processName(name: String?) {
    requireNotNull(name) { "Name must not be null" }
    // nameはスマートキャストでnon-null
    println(name.length)
}

// 状態のチェック
fun process() {
    val value = getSomeValue()
    checkNotNull(value) { "Value should not be null" }
    // 以降valueはnon-null
}

8. Sealed classでnull回避

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Bad: nullを使用
fun getUser(): User? {
    return if (exists) user else null
}

// Good: Sealed classで表現
sealed class UserResult {
    data class Success(val user: User) : UserResult()
    object NotFound : UserResult()
}

fun getUser(): UserResult {
    return if (exists) UserResult.Success(user) else UserResult.NotFound
}

9. Null Object パターン

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
interface Logger {
    fun log(message: String)
}

class ConsoleLogger : Logger {
    override fun log(message: String) = println(message)
}

object NullLogger : Logger {
    override fun log(message: String) { /* no-op */ }
}

// nullの代わりにNullLoggerを使用
val logger: Logger = getLogger() ?: NullLogger

Kotlinのnull安全機能

機能用途
?. (safe call)nullならnullを返す
?: (Elvis)nullならデフォルト値
!! (non-null assertion)nullならNPE(避けるべき)
letnull以外の場合に処理
also, runスコープ関数でnullハンドリング

よくある間違い

  • !!演算子を便利だからと多用する
  • Javaライブラリの戻り値をnullチェックしない
  • lateinitをlazyと混同する
  • nullableなコレクション要素のチェック忘れ

関連エラー

参考リンク

Kotlin の他のエラー

最終更新: 2025-12-13