[解決済み] by lazy" と "lateinit" を使ったプロパティの初期化
質問
Kotlinでは、コンストラクタ内やクラス本体の先頭でクラスのプロパティを初期化したくない場合、基本的に以下の2つの選択肢があります(言語リファレンスより)。
lazy()
のインスタンスを返す関数です。Lazy<T>
を最初に呼び出すと、遅延プロパティを実装するためのデリゲートとして機能します。get()
に渡されたラムダを実行します。lazy()
を呼び出すと、その結果を記憶します。get()
は、単に記憶された結果を返すだけです。例
public class Hello { val myLazyString: String by lazy { "Hello" } }
つまり、最初の呼び出しとそれに続く呼び出しは、どこであろうと
myLazyString
を返します。
Hello
通常、非 NULL 型として宣言されたプロパティは、コンストラクタで初期化する必要があります。しかし、多くの場合、これは便利ではありません。例えば、依存性注入やユニットテストのセットアップメソッドでプロパティを初期化することができます。この場合、コンストラクタで非 null のイニシャライザを指定することはできませんが、クラス本体でプロパティを参照する際に null チェックを行うことは避けたいものです。
このケースを処理するには、プロパティに lateinit 修飾子を付けるとよいでしょう。
public class MyTest { lateinit var subject: TestSubject @SetUp fun setup() { subject = TestSubject() } @Test fun test() { subject.method() } }
この修飾子は、クラス本体で宣言された var プロパティ (一次コンストラクタ内ではない) にのみ使用でき、そのプロパティがカスタムゲッターまたはカスタムセッターを持たない場合にのみ使用できます。プロパティの型は非 NULL でなければならず、プリミティブ型であってはなりません。
では、この2つの選択肢はどちらも同じ問題を解決することができるため、どのように選択するのが正しいのでしょうか?
どのように解決するのか?
以下は
lateinit var
と
by lazy { ... }
デリゲートプロパティ
-
lazy { ... }
デリゲートはval
プロパティがあるのに対しlateinit
にのみ適用可能です。var
にコンパイルすることができないためです。final
フィールドがあるため、不変性が保証されない。 -
lateinit var
は値を格納するバッキングフィールドを持ちby lazy { ... }
は、一度計算された値が格納されるデリゲートオブジェクトを生成し、クラスオブジェクトにデリゲートインスタンスへの参照を格納し、デリゲートインスタンスと連携するプロパティ用ゲッターを生成します。ですから、もしクラス内に存在するバッキングフィールドが必要な場合はlateinit
; -
に加えて
val
s,lateinit
は、NULL可能なプロパティやJavaのプリミティブ型には使用できません(これはnull
初期化されていない値に使用される)。 -
lateinit var
は、フレームワークのコード内部など、オブジェクトが見えるところから初期化することができ、1つのクラスの異なるオブジェクトに対して複数の初期化シナリオが可能です。by lazy { ... }
このプロパティは、サブクラスでプロパティをオーバーライドすることによってのみ変更することができます。もし、あなたのプロパティを外部から、おそらく事前に知られていない方法で初期化したい場合はlateinit
. -
初期設定
by lazy { ... }
はデフォルトでスレッドセーフであり、イニシャライザが最大一度だけ起動されることが保証されています (ただし、これは 別のlazy
オーバーロード ). の場合はlateinit var
マルチスレッド環境でプロパティを正しく初期化するかどうかは、ユーザーのコード次第です。 -
A
Lazy
のインスタンスは、保存、受け渡し、さらには複数のプロパティに使用することができます。逆にlateinit var
は、追加の実行時状態を保存しません(null
を初期化されていない値のフィールドに置く)。 -
のインスタンスへの参照を保持する場合、そのインスタンスは
Lazy
,isInitialized()
を使えば、すでに初期化されているかどうかを確認することができます(そして、その際に リフレクションでそのようなインスタンスを取得する をデリゲートされたプロパティから取得することができます)。lateinitプロパティが初期化されたかどうかを確認するには、次のようにします。 使用property::isInitialized
Kotlin 1.2以降 . -
に渡されるラムダ。
by lazy { ... }
は、それが使われているコンテキストからの参照を、 その クロージャ .. そして、その参照を保存し、プロパティが初期化されたときにのみ、参照を解放します。このため、Androidのアクティビティなどのオブジェクト階層があまりに長い間解放されない(あるいは、プロパティがアクセス可能なまま一度もアクセスされない場合)ことがあるので、初期化ラムダ内で何を使用するかについて注意する必要があります。
また、質問には書かれていない別の方法があります。
Delegates.notNull()
これは、Javaのプリミティブ型を含む、非Nullプロパティの遅延初期化に適しています。
関連
-
[解決済み] リフレクションを使用して文字列からプロパティ値を取得する
-
[解決済み] リモートDB2データベースに接続できないことがある
-
[解決済み] フィールドとプロパティの違いは何ですか?
-
[解決済み] Pythonの@propertyデコレーターはどのように機能するのでしょうか?
-
[解決済み] オブジェクトのプロパティを値でソートする
-
[解決済み] 変数を使用してオブジェクトのプロパティに動的にアクセスする
-
[解決済み] プロパティとゲッターとセッターの使い分け
-
[解決済み] lateinit "変数が初期化されたかどうかを確認する方法は?
-
[解決済み] by lazy" と "lateinit" を使ったプロパティの初期化
-
[解決済み】Swiftのクラスでエラー。super.initの呼び出しでプロパティが初期化されていない
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン