注用というコンパイル時の定数が可変に任意の通常のJavaアクセスのために、主張されている一部の人々とは異なり、初期値の順序問題に影響されません。
私たちは、次の例によりこれを示すことができ
:
abstract class Base {
Base() {
// bad coding style don't do this in real code
printValues();
}
void printValues() {
System.out.println("var1 read: "+getVar1());
System.out.println("var2 read: "+getVar2());
System.out.println("var1 via lambda: "+supplier1().get());
System.out.println("var2 via lambda: "+supplier2().get());
}
abstract String getVar1();
abstract String getVar2();
abstract Supplier<String> supplier1();
abstract Supplier<String> supplier2();
}
public class ConstantInitialization extends Base {
final String realConstant = "a constant";
final String justFinalVar; { justFinalVar = "a final value"; }
ConstantInitialization() {
System.out.println("after initialization:");
printValues();
}
@Override String getVar1() {
return realConstant;
}
@Override String getVar2() {
return justFinalVar;
}
@Override Supplier<String> supplier1() {
return() -> realConstant;
}
@Override Supplier<String> supplier2() {
return() -> justFinalVar;
}
public static void main(String[] args) {
new ConstantInitialization();
}
}
それは出力しますので、
var1 read: a constant
var2 read: null
var1 via lambda: a constant
var2 via lambda: null
after initialization:
var1 read: a constant
var2 read: a final value
var1 via lambda: a constant
var2 via lambda: a final value
、あなたが見ることができるよう、realConstant
フィールドへの書き込みはまだ実現しなかったという事実をスーパーコンストラクタが実行されたときに、たとえラムダ式を介してアクセスしたとしても、真のコンパイル時定数の初期化されていない値は見られません。技術的には、フィールドは実際には読み取られないためです。
また、不快なReflectionハックは、同じ理由で通常のJavaアクセス時にコンパイル時定数に影響を与えません。
- リフレクションもには影響しません、私たちは二つのことを示して
lambda: foo
captured obj: true
via Reflection: bar
ordinary field access: foo
を:それは印刷し
public class TestCapture {
static class MyClass {
final String foo = "foo";
private Consumer<String> getFn() {
//final String localFoo = foo;
return bar -> System.out.println("lambda: " + bar + foo);
}
}
public static void main(String[] args) throws ReflectiveOperationException {
final MyClass obj = new MyClass();
Consumer<String> fn = obj.getFn();
// change the final field obj.foo
Field foo=obj.getClass().getDeclaredFields()[0];
foo.setAccessible(true);
foo.set(obj, "bar");
// prove that our lambda expression doesn't read the modified foo
fn.accept("");
// show that it captured obj
Field capturedThis=fn.getClass().getDeclaredFields()[0];
capturedThis.setAccessible(true);
System.out.println("captured obj: "+(obj==capturedThis.get(fn)));
// and obj.foo contains "bar" when actually read
System.out.println("via Reflection: "+foo.get(capturedThis.get(fn)));
// but no ordinary Java access will actually read it
System.out.println("ordinary field access: "+obj.foo);
}
}
:バックな修正値を読み込むための唯一の方法は、リフレクション経由でコンパイル時定数
- 周囲のオブジェクトは、使用されなくてもキャプチャされています。
インスタンスフィールドへのアクセスには、には、フィールドが実際には読み取られない場合でも、そのフィールドのインスタンスを取得するためのλ式がである必要がありますが、残念ながら私は少し恐ろしいです現在のJava言語仕様、内の値の取り込みやthis
に関するいかなる文を見つけることができませんでした:
我々は、ラムダ式でないアクセスするインスタンスフィールドを作成するという事実に慣れthis
への参照を持たないインスタンスですが、それでも実際には保証されていませんb現在の仕様です。この省略はすぐに修正されることが重要です...
あなたは明確にすることができますか?最初に、あなたが '' MyClass''インスタンスを囲むことを期待していると説明し、その後、あなたはこの '' args $ 1''を見て驚いています。 –
@JeanLogaert質問は*最終的な変数を使っている間に*どうして*なぜですか? –
@JeanLogeart質問定数として扱われる 'final String'に特に関連しています。 – manouti