Kotlin では原則 Null 非許容であるため、通常 var や val のプロパティは、宣言時にすぐに初期化されなければならない
ただ、lateinit という修飾子を使えばプロパティの初期化を後回しにすることができることを知った
ところで、この折角の Null 非許容を破ってしまうlateinitはいつ使うべきなのか、使い所がわからなかったので調べた
lateinit の基本的な特徴
varにのみ使用できる:lateinitはvar変数にのみ使用可能で、val変数には使えない。つまり、lateinitプロパティは再代入が可能- プリミティブ型には使用不可:
Int、Boolean、Doubleなどのプリミティブ型には使えない。lateinitはオブジェクト型に対してのみ使用可能 - null 許容型ではない:
lateinitプロパティはnull許容型ではなく、必ず後で非nullの値が代入されることを期待している。もし初期化前にアクセスすると、UninitializedPropertyAccessExceptionがスローされる
lateinit を使うべき場面
lateinit は、プロパティを後から初期化する必要がある場合に使われる
これにより、コンストラクタ内で必ず初期化できない状況や、初期化を遅らせたい場合に便利
1. 依存性注入(Dependency Injection)
依存性注入フレームワークを使う場合、プロパティを後で初期化する必要がある
通常、DI フレームワークはオブジェクト生成後にプロパティの依存関係を注入するため、Kotlin の通常の初期化ルールに従わないことがある
class MyService { lateinit var database: Database fun initialize() { database.connect() } }
この例では、database プロパティはオブジェクト生成時にはまだ初期化されておらず、後で依存性注入で設定される
2. Android 開発
lateinit は、Android 開発において UI 要素を後で初期化するためによく使われるらしい
Android開発はしたことないが、Activity や Fragment の onCreate メソッドの中で ビューの初期化が行われることが一般らしく、その時点でプロパティを初期化したい場合に lateinit が便利とのこと
class MainActivity : AppCompatActivity() { lateinit var textView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) textView = findViewById(R.id.text_view) // 後で初期化 textView.text = "Hello, World!" } }
textView は onCreate 内で初期化されるため、宣言時に lateinit を使っている
これにより、null 許容型を使わずに、初期化の遅延を実現している
3. ユニットテスト
テスト時にプロパティをモックオブジェクトやテストデータで初期化する場合
プロパティをテストのセットアップ中に初期化するために、lateinit を使う
class MyTest { lateinit var mockService: MyService @Before fun setUp() { mockService = mock(MyService::class.java) } @Test fun testService() { mockService.doSomething() // テストコード... } }
上の例ではテスト前のセットアップで mockService を初期化しており、テストの実行時に lateinit を使って初期化が遅延されている
初期化確認の方法
lateinit プロパティが初期化されたかどうかを確認する方法として、::プロパティ名.isInitialized を使うことができる
これにより、プロパティが初期化されているかを確認し、未初期化の場合に例外を防ぐことが可能
class MyClass { lateinit var name: String fun printName() { if (::name.isInitialized) { println(name) } else { println("name is not initialized") } } }