以前勉強会で「Kotlinのlateinit
とDelegates.notNull()
の使い分けがよく分からない」というお話をいただいた時に"基本的にはDelegates.notNull()
を使うべきではないか?" と答えたしそれより前から最近もずっと思っていたのだが、(DroidKaigiの発表を聞くなどする感じ)どうも違うのではないかと思えてきた。
そのため、ここでlateinitの仕様について整理しつつ、そこから導き出される行儀の良い使い方を考察する。
結論
- kaptを利用してJavaのコードからフィールドを操作したい場合は
lateinit
を使うべき- フィールドが露出すると不都合がある場合は
Delegates.notNull()
を使うべき
- フィールドが露出すると不都合がある場合は
lateinit
を普通に遅延初期化目的で使うならprivateで使うべき- 遅延初期化するが、タイミングは最初にアクセスされた時でよい&変更されないなら
lazy
で十分 - 小さなオブジェクトが大量に生成されると困るくらいパフォーマンスを気にする部分では
lateinit
を使うとよい
lateinitとは
KotlinでNonNullなプロパティーの初期化をconstructorより後に遅らせられる機能。
元々はkaptで(例えば)Dagger2からインスタンスをプロパティーに直接代入できない、みたいなことを解決するアプローチとして追加された。
lateinitの使い方
lateinit
をプロパティー定義の前に書く。
lateinit var viewModel: HogeViewModel fun onCreate(savedInstanceState: Bundle?) { // DI component.inject(this) viewModel.fuga // インスタンスにアクセスできる }
制約
- valには使えない(以前は使えたが今は使えない)
- Nullableには使えない
- プリミティブ型には使えない
- 使うことでJavaからはそのプロパティーのfieldがプロパティーと同じvisibilityで見える
- private 以外にすると外から見える可能性がある → 意図しない使い方になる可能性
Delegates.notNull() との違い
lateinit
はDelegateオブジェクトが作られないので、大量に使う場合にパフォーマンス的に有利Delegates.notNull()
はプリミティブ型にも使える
lazyとの違い
lazy
は値をsetできない(1回限りの遅延初期化)lazy
はNullableでも使える
最後に
意外とlateinitもっとカジュアルに使ってもいいのかもしれない。