Nota che per ogni accesso Java ordinaria ad una variabile essendo una fase di compilazione costante, il valore costante ha luogo, quindi, a differenza di quanto affermato da alcune persone, è immune ai problemi di ordine di inizializzazione.
Possiamo dimostrare questo dal seguente esempio:
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();
}
}
Esso stampa:
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
Quindi, come si può vedere, il fatto che la scrittura al campo realConstant
non aveva ancora succedere quando viene eseguito il super costruttore, non viene visualizzato alcun valore non inizializzato per la costante costante in fase di compilazione, anche quando si accede tramite espressione lambda. Tecnicamente, perché il campo non è effettivamente letto.
Inoltre, i brutti attacchi di Reflection non hanno alcun effetto sull'ordinario accesso Java alle costanti di compilazione, per lo stesso motivo. L'unico modo per leggere un tale valore modificato di nuovo, è via Riflessione:
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);
}
}
Esso stampa:
lambda: foo
captured obj: true
via Reflection: bar
ordinary field access: foo
che ci mostra due cose,
- riflessione ha anche alcun effetto sulla costanti di tempo di compilazione
- L'oggetto circostante è stato catturato, nonostante non verrà utilizzato
Sarei felice di trovare una spiegazione del tipo "qualsiasi accesso a un campo di istanza richiede l'espressione lambda per acquisire l'istanza di quel campo (anche se il campo non viene effettivamente letto)", ma purtroppo io non riusciva a trovare qualsiasi dichiarazione per quanto riguarda l'acquisizione di valori o di this
nel corrente linguaggio Java Specification, che è un po 'spaventoso:
Ci siamo abituati al fatto che non accesso ai campi di istanza in un'espressione lambda creerà un'istanza che non ha un riferimento a this
, ma anche questo non è effettivamente garantito b y la specifica attuale. È importante che questa omissione venga risolta presto ...
Puoi chiarire?Per prima cosa spiega che ci si aspetta l'istanza di '' MyClass'' e dopo si è sorpresi di vedere questo '' args $ 1'' –
@JeanLogaert La domanda è * perché * si verifica ancora mentre si utilizza una variabile finale –
@JeanLogeart La domanda si riferisce specificamente a 'final String', che è considerato una costante. – manouti