Slide 1

Slide 1 text

フィールドの遅延初期化 櫻庭 祐一 カサレアル

Slide 2

Slide 2 text

未初期化状態に アクセス可能 なのに ではない オブジェクト生成時に 必ず初期化が必要

Slide 3

Slide 3 text

未初期化状態に アクセス可能 なのに ではない オブジェクト生成時に 必ず初期化が必要 なぜ今になって 問題が顕在化??

Slide 4

Slide 4 text

プログラミングスタイルの変化

Slide 5

Slide 5 text

プログラミングスタイルの変化 にするには すべてのフィールドを 状態変更 オプション

Slide 6

Slide 6 text

未初期化状態に アクセス可能 なのに ではない オブジェクト生成時に 必ず初期化が必要

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

フィールドの初期化 宣言時 final String text = ”Hello”; イニシャライザー final String text; { text = ”Hello”; } コンストラクタ final String text; Foo() { text = ”Hello”; } これ以外で代入するとコンパイルエラー

Slide 9

Slide 9 text

フィールドの初期化 初期化に時間がかかる場合 外部リソースの使用 ファイル、通信、DB接続 同期化が必要な場合 起動時のパフォーマンス低下 実際に使う時まで初期化を遅延させたい... でも、できない そこで

Slide 10

Slide 10 text

フィールドの遅延初期化のためのインタフェース 初期化には を使用 は のサブインタフェース スレッドセーフ による最適化 の から変更

Slide 11

Slide 11 text

メソッド 生成 値の取得 初回コールで値の初期化 注 の使用後に使う だけでは初期化されない

Slide 12

Slide 12 text

class Consumer { final LazyConstant heavyConstant = LazyConstant.of(() -> new Heavy()); void consume() { Heavy heavy = heavyConstant.get(); ...... } ...... } で値の生成 初回コール時に を実行 回目からは単に値を返す

Slide 13

Slide 13 text

で値の生成 初回コール時に を実行 回目からは単に値を返す class Consumer { final LazyConstant heavyConstant = LazyConstant.of(Heavy::new); void consume() { Heavy heavy = heavyConstant.get(); ...... } ...... }

Slide 14

Slide 14 text

初期化時に例外が発生する場合 で を返す コール時に がスローされる ただし スタックトレースは切れる で をスロー コール時に例外対応 で を返す コール時に例外をチェック

Slide 15

Slide 15 text

アイデアは と同じ class Singleton { static Singleton instance; private Singleton() { ... } Singleton get() { if (instance == null) { instance = new Singleton(); } return instance; } ...... }

Slide 16

Slide 16 text

アイデアは と同じ class Singleton { static Singleton instance; private Singleton() { ... } synchronized Singleton get() { if (instance == null) { instance = new Singleton(); } return instance; } ...... } しかし、スケールしない

Slide 17

Slide 17 text

アイデアは と同じ class Singleton { static volatile Singleton instance; private Singleton() { ... } Singleton get() { if (instance == null) { synchronized (this) { if (instance == null) { instance = new Singleton(); } } } return instance; }

Slide 18

Slide 18 text

public final class LazyConstantImpl implements LazyConstant { @Stable private T constant; @ForceInline @Override public T get() { final T t = getAcquire(); return (t != null) ? t : getSlowPath(); } public final class LazyConstantImpl implements LazyConstant { @Stable private T constant; @ForceInline @Override public T get() { final T t = getAcquire(); return (t != null) ? t : getSlowPath(); }

Slide 19

Slide 19 text

private T getSlowPath() { preventReentry(); synchronized (this) { T t = getAcquire(); if (t == null) { t = computingFunction.get(); Objects.requireNonNull(t); setRelease(t); // Allow the underlying supplier to be collected after successful use computingFunction = null; } return t; } } private T getSlowPath() { preventReentry(); synchronized (this) { T t = getAcquire(); if (t == null) { t = computingFunction.get(); Objects.requireNonNull(t); setRelease(t); // Allow the underlying supplier to be collected after successful use computingFunction = null; } return t; } }

Slide 20

Slide 20 text

private T getSlowPath() { preventReentry(); synchronized (this) { T t = getAcquire(); if (t == null) { t = computingFunction.get(); Objects.requireNonNull(t); setRelease(t); // Allow the underlying supplier to be collected after successful use computingFunction = null; } return t; } } の実行

Slide 21

Slide 21 text

フィールド以外にも遅延初期化は可能 ただしミュータブル変数は最適化の恩恵にあずかれない のリトライは の内部で行う を複数回コールしても、 一度失敗した初期化はリトライしない コレクションの遅延初期化

Slide 22

Slide 22 text

まとめ フィールドの遅延初期化 スレッドセーフ 初期化を遅延させても最適化の恩恵あり コレクションの遅延初期化

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

スーパークラスのコンストラクターは サブクラスのコンストラクターの先頭 class B extends A { B() { super(); // その他の処理 } }

Slide 25

Slide 25 text

スーパークラスのコンストラクターは サブクラスのコンストラクターの先頭 class B extends A { B() { super(); // その他の処理 } } からコンストラクターのどこでも class B extends A { B() { // フィールドの初期化 super(); // その他の処理 } } ただし は使用不可 インスタンスメソッド使用不可 フィールドの参照は使用不可 などの制約あり

Slide 26

Slide 26 text

class A { ... } class B extends A { ... } new B(); B() A() Object() A フィールド 初期化 B フィールド 初期化 この時点まで のフィールド未初期化 かつアクセス可能

Slide 27

Slide 27 text

class A { ... } class B extends A { ... } A() フィールド 初期化 B() フィールド 初期化 new B(); Object() A 残処理 B 残処理 で対応済み

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

が でない リフレクション でも再代入可 最適化を阻害 , セキュリティの問題 将来的に の再代入のデフォルトは禁止 ではデフォルトが警告 --enable-final-field-mutation=module --illegal-final-field-mutation=deny/warn/allow /debugwarn

Slide 30

Slide 30 text

まとめ フィールドの つの問題を解決 イミュータブルの重要性 では 未統合 試すには独自ビルドが必要