【Kotlin】コピペで使える「null」を消すテクニック頻出順40

 

🧑🏻‍💻 1. Non-null を基本にする


// 悪い
val name: String?

// 良い
val name: String

Nullable を作らないことが最も重要。

 

🧑🏻‍💻 2. DTOだけ Nullable


data class UserResponse(
    val name: String?
)

外部データだけ Nullable にする。

 

🧑🏻‍💻 3. Domain は Non-null


data class User(
    val name: String
)

アプリ内部では Non-null を維持する。

 

🧑🏻‍💻 4. Repositoryで吸収する


fun load(): User =
    api.load()?.toUser() ?: User.EMPTY

Repository より先へ Nullable を持ち込まない。

 

🧑🏻‍💻 5. デフォルト値を使う


data class User(
    val name: String = ""
)

初期値で null を不要にする。

 

🧑🏻‍💻 6. orEmpty()


val name = user.name.orEmpty()

null を空文字へ変換する。

 

🧑🏻‍💻 7. emptyList()


fun users(): List<User> = emptyList()

List? を返さない。

 

🧑🏻‍💻 8. emptyMap()


fun settings(): Map<String, String> = emptyMap()

Map? を返さない。

 

🧑🏻‍💻 9. emptySet()


fun tags(): Set<String> = emptySet()

Set? を返さない。

 

🧑🏻‍💻 10. ?: return


val user = repository.find(id) ?: return

null を早期に除去する。

 

🧑🏻‍💻 11. ?: continue


val name = user.name ?: continue

ループ中の null をスキップする。

 

🧑🏻‍💻 12. ?: break


val item = iterator.nextOrNull() ?: break

null を終了条件として扱う。

 

🧑🏻‍💻 13. ?: error()


val token = token ?: error("Token required")

あり得ない null は即座に失敗させる。

 

🧑🏻‍💻 14. mapNotNull()


val names = users.mapNotNull { it.name }

変換と null 除去を同時に行う。

 

🧑🏻‍💻 15. filterNotNull()


val names = names.filterNotNull()

Collection の null を取り除く。

 

🧑🏻‍💻 16. Flow filterNotNull()


val users: Flow<User> =
    repository.userFlow.filterNotNull()

Flow の null を流さない。

 

🧑🏻‍💻 17. firstOrNull() ?: return


val user = users.firstOrNull() ?: return

最初の要素が無ければ早期終了する。

 

🧑🏻‍💻 18. by lazy


private val repository by lazy {
    UserRepository()
}

遅延初期化で Nullable を不要にする。

 

🧑🏻‍💻 19. Smart Cast(ローカル変数へ退避)


val user = currentUser ?: return

println(user.name)

Smart Cast を有効にして !! を避ける。

 

🧑🏻‍💻 20. StateFlow を Nullable にしない


private val uiState =
    MutableStateFlow(UserUiState())

StateFlow より状態オブジェクトを持つ。

 

🧑🏻‍💻 21. Compose State を Nullable にしない


var uiState by mutableStateOf(UserUiState())

mutableStateOf より状態オブジェクトを持つ。

 

🧑🏻‍💻 22. Java境界で Non-null 化する


val title: String =
    javaApi.title.orEmpty()

Platform Type (String!) を入口で閉じ込める。

 

🧑🏻‍💻 23. requireNotNull()


val user = requireNotNull(user)

引数や入力値の null を早期に拒否する。

 

🧑🏻‍💻 24. checkNotNull()


val database = checkNotNull(database)

オブジェクトの状態が不正なら失敗させる。

 

🧑🏻‍💻 25. Null Object Pattern


interface Logger

object EmptyLogger : Logger

Logger? の代わりに空実装を使う。

 

🧑🏻‍💻 26. sealed class / interface


sealed interface UiState {
    data object Loading : UiState
    data class Success(val users: List<User>) : UiState
    data class Error(val message: String) : UiState
}

null の代わりに状態を型で表現する。

 

🧑🏻‍💻 27. Result


fun load(): Result<User>

失敗を null ではなく Result で表現する。

 

🧑🏻‍💻 28. value class


@JvmInline
value class UserId(val value: Long)

IDなどを専用型にして null を減らす。

 

🧑🏻‍💻 29. takeIf()


val adult = user.takeIf { it.age >= 18 } ?: return

条件を満たさなければ早期終了する。

 

🧑🏻‍💻 30. firstNotNullOf()


val token = providers.firstNotNullOf {
    it.token
}

最初に見つかった Non-null 値を取得する。

 

🧑🏻‍💻 31. SharedFlow を使う


private val events = MutableSharedFlow<Event>(
    replay = 1
)

イベントでは StateFlow より SharedFlow が適している場合がある。

 

🧑🏻‍💻 32. Nothing を使う


fun fail(message: String): Nothing =
    throw IllegalStateException(message)

val token = token ?: fail("Token required")

Nothing を使うと独自のガード関数を作れる。

 

🧑🏻‍💻 33. !! を使わない


// 悪い
val user = user!!

// 良い
val user = requireNotNull(user)

!! より意図が明確な API を使う。

 

🧑🏻‍💻 34. let を減らす


// 悪い
user?.let {
    println(it.name)
}

// 良い
val user = user ?: return
println(user.name)

ネストを減らして読みやすくする。

 

🧑🏻‍💻 35. lateinit


private lateinit var repository: UserRepository

後から初期化する参照型では Nullable を避けられる。

 

🧑🏻‍💻 36. Delegates.notNull()


var count: Int by Delegates.notNull()

Int や Boolean など lateinit が使えない型向け。

 

🧑🏻‍💻 37. takeUnless()


val user = user.takeUnless { it.deleted } ?: return

条件を満たしたら除外したい場合に使う。

 

🧑🏻‍💻 38. firstNotNullOfOrNull()


val token = providers.firstNotNullOfOrNull {
    it.token
}

最初の Non-null を取得し、無ければ null を返す。

 

🧑🏻‍💻 39. Kotlin Contracts


@OptIn(ExperimentalContracts::class)
fun requireUser(user: User?) {
    contract {
        returns() implies (user != null)
    }
    requireNotNull(user)
}

独自関数でも Smart Cast を効かせられる。

 

🧑🏻‍💻 40. associateNotNull() パターン


val map = users
    .mapNotNull {
        it.id?.let { id -> id to it }
    }
    .toMap()

Map を作る途中で null を除外する。

 

🧑🏻‍💻 まとめ

「null」を未だに使ってるプロジェクト、実際現場では多いですよね。

「NullPointerExeption(ヌルポ)」の対応による開発時間の損失は、IT業界全体で年間数十兆円、個々の開発プロジェクトでも全体の数割の時間を奪うレベルの巨大なインパクトを持っています。


Related Categories :  AndroidDevelopmemtJavascriptKotlinNewbieTerrible