ProgrammingAndroid開発者

Kotlinの「lateinit」と「lazy」とは何ですか?それらの違い、使用のニュアンス、制限について説明し、関連するコード例を示してください。

Hintsage AIアシスタントで面接を突破

回答

Kotlinには、プロパティの遅延初期化のための2つの異なるアプローチがあります:lateinitlazy

**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) { ... }と適切なパラメータを渡しました。