Beachten Sie, dass für jeden gewöhnlichen Java Zugriff auf eine Variable eines Kompilierung-Konstante ist, der konstante Wert findet statt, so dass er im Gegensatz zu einigen behaupteten Personen immun gegen Probleme mit der Initialisierungsreihenfolge ist.
Wir können dies durch das folgende Beispiel zeigen:
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();
}
}
Er druckt:
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
So, wie Sie, die Tatsache sehen, dass der Schreibvorgang in das realConstant
Feld noch nicht geschehen Wenn der Superkonstruktor ausgeführt wird, wird kein nicht initialisierter Wert für die wahre Kompilierzeitkonstante angezeigt, selbst wenn über den Lambda-Ausdruck darauf zugegriffen wird. Technisch, weil das Feld nicht wirklich gelesen wird.
Außerdem haben aus dem gleichen Grund unangenehme Reflection-Hacks keine Auswirkungen auf den normalen Java-Zugriff auf Kompilierzeitkonstanten. Der einzige Weg, einen solchen modifizierten Wert wieder zu lesen, ist über Reflexion:
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);
}
}
Er druckt:
lambda: foo
captured obj: true
via Reflection: bar
ordinary field access: foo
, die uns zwei Dinge zeigt,
- Reflexion hat auch keinen Einfluss auf Kompilierzeitkonstanten
- Das umgebende Objekt wurde erfasst, obwohl es nicht verwendet wird
Ich würde mich freuen, eine Erklärung wie zu finden, "jeder Zugriff auf ein Instanzfeld erfordert der Lambda-Ausdruck, um die Instanz dieses Feldes zu erfassen (auch wenn das Feld nicht tatsächlich gelesen wird)", aber leider ich konnte nicht jeden Aussage über Erfassung von Werten oder this
in der aktuellen Java Language Specification finden, die ein bisschen erschreckend ist:
wir daran gewöhnt, dass nicht Instanzfelder in einem Lambda-Ausdruck Zugriff schaffen wird eine Instanz, die keinen Bezug auf this
hat, aber selbst das ist nicht garantiert b y die aktuelle Spezifikation. Es ist wichtig, dass diese Unterlassung bald behoben wird ...
Können Sie klären?Sie erklären zuerst, dass Sie die umschließende '' MyClass''-Instanz erwarten und danach sind Sie überrascht, diese '' args $ 1'' zu sehen –
@JeanLogaert Die Frage ist * warum * es passiert immer noch bei der Verwendung einer endgültigen Variablen –
@JeanLogeart Die Frage bezieht sich speziell auf "final String", die als Konstante betrachtet wird. – manouti