このオブジェクトのコンストラクターが終了する前に、オブジェクトへの参照が値を受け取ることがあるJMMのこの機能については、誰もが知っています。
JLS7では、p 。17.5最終的なフィールド セマンティクスも読むことができます。
フィールドの使用モデル
final
は単純final
です。オブジェクトのコンストラクターでオブジェクトのフィールドを設定します。オブジェクトのコンストラクターが終了する前に、別のスレッドが参照できる場所に構築中のオブジェクトへの参照を書き込まないでください。これに従った場合、オブジェクトが別のスレッドによって認識されると、そのスレッドは常に、そのオブジェクトのfinal
フィールドの正しく構築されたバージョンを認識します。(1)
そして、その直後に JLS の例が続きます。これは、non-finalフィールドがどのように初期化されることが保証されていないかを示しています (1Example 17.5-1.1) (2)
:
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}
また、この質疑応答でグレイ氏は次のように書いています。
フィールドを としてマークすると
final
、コンストラクターはコンストラクターの一部として初期化を完了することが保証されます。それ以外の場合は、ロックを使用する前にロックを同期する必要があります。(3)
したがって、質問は次のとおりです。
1) ステートメント (1) によると、コンストラクターが終了する前に不変オブジェクトへの参照を共有することは避けるべきです
2) JLS の例 (2) と結論 (3) によると、不変オブジェクトのコンストラクターが終了する前、つまりすべてのフィールドがfinal
.
何か矛盾していませんか?
EDIT-1 : 私が正確に言いたいこと。そのような方法で例のクラスを変更すると、そのフィールドy
もfinal
(2) になります。
class FinalFieldExample {
final int x;
final int y;
...
したがって、reader()
メソッドでは、次のことが保証されます。
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // guaranteed to see 4, isn't it???
f
もしそうなら、コンストラクターが終了する前に ((1) に従って)オブジェクトへの参照を書き込むことを避ける必要があるのはなぜf
ですか?