Mi piacerebbe capire uno strano comportamento che ho dovuto affrontare con le classi anonime.Java: inizializzazione e costruttore di classi anonime
Ho una classe che chiama un metodo protetto all'interno del suo costruttore (lo so, il design povero, ma questa è un'altra storia ...)
public class A {
public A() {
init();
}
protected void init() {}
}
poi ho un'altra classe che estende A
e sovrascrive init()
.
public class B extends A {
int value;
public B(int i) {
value = i;
}
protected void init() {
System.out.println("value="+value);
}
}
Se il codice
B b = new B(10);
ottengo
> value=0
e che ci si aspetta, perché il costruttore della classe Super viene invocato prima del B
ctor e poi value
è ancora.
Ma quando si utilizza una classe anonima come questo
class C {
public static void main (String[] args) {
final int avalue = Integer.parsetInt(args[0]);
A a = new A() {
void init() { System.out.println("value="+avalue); }
}
}
}
mi aspetto di ottenere value=0
perché questo dovrebbe essere più o meno uguale alla classe B
: il compilatore crea automaticamente una nuova classe C$1
che si estende A
e crea variabili di istanza per memorizzare le variabili locali fanno riferimento i metodi della classe anonima, simulando una chiusura ecc ...
Ma quando si esegue questo, ho avuto
> java -cp . C 42
> value=42
Inizialmente pensavo che questo fosse dovuto al fatto che stavo usando java 8, e forse, introducendo lamdbas, hanno cambiato il modo in cui le classi anonime sono implementate sotto il cofano (non hai più bisogno di final
), ma ho provato con Java 7 anche ed ho ottenuto lo stesso risultato ...
in realtà, guardando il codice byte con javap
, vedo che è B
> javap -c B
Compiled from "B.java"
public class B extends A {
int value;
public B(int);
Code:
0: aload_0
1: invokespecial #1 // Method A."<init>":()V
4: aload_0
5: iload_1
6: putfield #2 // Field value:I
9: return
...
mentre per C$1
:
> javap -c C\$1
Compiled from "C.java"
final class C$1 extends A {
final int val$v;
C$1(int);
Code:
0: aload_0
1: iload_1
2: putfield #1 // Field val$v:I
5: aload_0
6: invokespecial #2 // Method A."<init>":()V
9: return
....
Qualcuno potrebbe dirmi perché questa differenza? Esiste un modo per replicare il comportamento della classe anonima usando classi "normali"?
EDIT: per chiarire la questione: perché l'inizializzazione delle classi anonime rompere le regole per l'inizializzazione di qualsiasi altra classe (dove costruttore di eccellente viene invocato prima di impostare qualsiasi altra variabile)? Oppure, c'è un modo per impostare la variabile di istanza nella classe B
prima di inovking super-costruttore?
Perché pensi che il tuo primo e il secondo codice siano gli stessi? Nel secondo codice, stai accedendo alla variabile locale. Questo verrà inizializzato prima dell'esecuzione dell'istruzione della classe anonima. –
umh ... ok, tu dici: il fatto che il compilatore crei una classe per implementare questo scenario dovrebbe essere nascosto allo sviluppatore, quindi la classe 'C $ 1' è un caso speciale, e va bene se non segue lo standard regole del costruttore. Questo è abbastanza ragionevole, ma comunque, imho è un po 'imbarazzante. – ugo