Takuji->find;

株式会社はてなでアプリケーションエンジニアやってます、技術的な記事を書いているつもり

lateinitの行儀の良い使い方

以前勉強会で「KotlinのlateinitDelegates.notNull()の使い分けがよく分からない」というお話をいただいた時に"基本的にはDelegates.notNull()を使うべきではないか?" と答えたしそれより前から最近もずっと思っていたのだが、(DroidKaigiの発表を聞くなどする感じ)どうも違うのではないかと思えてきた。

そのため、ここでlateinitの仕様について整理しつつ、そこから導き出される行儀の良い使い方を考察する。

結論

  • kaptを利用してJavaのコードからフィールドを操作したい場合はlateinitを使うべき
    • フィールドが露出すると不都合がある場合はDelegates.notNull()を使うべき
  • lateinitを普通に遅延初期化目的で使うならprivateで使うべき
  • 遅延初期化するが、タイミングは最初にアクセスされた時でよい&変更されないならlazyで十分
  • 小さなオブジェクトが大量に生成されると困るくらいパフォーマンスを気にする部分ではlateinitを使うとよい

lateinitとは

KotlinでNonNullなプロパティーの初期化をconstructorより後に遅らせられる機能。

元々はkaptで(例えば)Dagger2からインスタンスをプロパティーに直接代入できない、みたいなことを解決するアプローチとして追加された。

blog.jetbrains.com

lateinitの使い方

lateinit をプロパティー定義の前に書く。

lateinit var viewModel: HogeViewModel

fun onCreate(savedInstanceState: Bundle?) {
   // DI
   component.inject(this)

   viewModel.fuga // インスタンスにアクセスできる
}

制約

  • valには使えない(以前は使えたが今は使えない)
  • Nullableには使えない
  • プリミティブ型には使えない
  • 使うことでJavaからはそのプロパティーのfieldがプロパティーと同じvisibilityで見える
    • private 以外にすると外から見える可能性がある → 意図しない使い方になる可能性

Delegates.notNull() との違い

  • lateinitDelegateオブジェクトが作られないので、大量に使う場合にパフォーマンス的に有利
  • Delegates.notNull() はプリミティブ型にも使える

lazyとの違い

  • lazyは値をsetできない(1回限りの遅延初期化)
  • lazyはNullableでも使える

最後に

意外とlateinitもっとカジュアルに使ってもいいのかもしれない。