Kotlinには、プロパティの遅延初期化のための2つの異なるアプローチがあります:lateinitとlazy。
**lateinit**は、後で初期化される変数(var)に適用される修飾子であり、通常はDIまたは初期化ブロックで初期化されます。NULLではないオブジェクトタイプの非静的変数にのみ適用されます。初期化前にアクセスすると、UninitializedPropertyAccessExceptionがスローされます。
class MyClass { lateinit var data: String fun init() { data = "Hello!" } }
**lazy**は、valプロパティ(読み取り専用)のために適用される特別なデリゲートです。初期化式は、変数への最初のアクセス時に評価されます。デフォルトでスレッドセーフです。
val config: Config by lazy { loadConfigFromFile() }
lateinitはプリミティブ型およびvalでは機能しません。lazyは再初期化を許可しません。lateinitは、遅れて必ず割り当てられる変数のためのもので(例えば、DIフレームワークやAndroidのライフサイクル内で)、lazyは計算が高コストであり、必要ない可能性がある場合に、遅延計算のために使用されます。
Int型のプロパティにlateinitを使用できますか?
回答: いいえ。lateinitは非NULLのオブジェクトタイプでのみ機能します。プリミティブおよびNULL型には使用できません。lateinit var a: Intを宣言しようとすると、コンパイルエラーが発生します。
事例
教育プロジェクトで、設定の変数をlateinit var config: Config?として宣言しましたが、コンパイラがそれを許可しませんでした — 時間を節約しましたが、期待通りに動作しない理由を理解する必要がありました。
事例
Androidアプリケーションで、呼び出し前にlateinitプロパティが初期化されているか確認するのを忘れました。その結果、アプリケーションが本番環境でクラッシュしました — デザイナーがonCreateViewの前にプロパティにアクセスしようとしました。追加の確認が助けになりました。
事例
マルチスレッドの利用時に、開発者はリソース集約型オブジェクトの初期化にby lazyを使用し、デフォルトのデリゲートがスレッドセーフでないと考え、パラメータを渡さずに行いました。その結果、リリースにおいてレースコンディションの問題が発生しました。それを解決するために、by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { ... }と適切なパラメータを渡しました。