PDA

View Full Version : [Java] inner-class anonima e variabili final


andbin
03-04-2007, 17:01
Premessa: una inner-class anonima all'interno di un metodo non può accedere alle variabili locali del metodo .... a meno che la variabile sia marcata final.
Su questo non ci piove. Volevo però capire bene cosa succede "dietro le quinte" per via del final.

Ho scritto un breve programma di esempio, del tutto inutile ....
public class Prova
{
public static void main (String[] args)
{
Prova p = new Prova ();
Object o = p.create ("Hello");
System.out.println (o);
}

public Object create (final String str)
{
return new Object () {
private String text = str;

public String toString () {
return "Anonymous, text=" + text;
}
};
}
}Poi con il tool 'jad' ho decompilato il file Prova.class e poi il file Prova$1.class. Ho ottenuto questo:
import java.io.PrintStream;

public class Prova
{
public Prova()
{
}

public static void main(String args[])
{
Prova prova = new Prova();
Object obj = prova.create("Hello");
System.out.println(obj);
}

public Object create(final String str)
{
return new Object() {

public String toString()
{
return (new StringBuilder()).append("Anonymous, text=").append(text).toString();
}

private String text;
final String val$str;
final Prova this$0;

{
this$0 = Prova.this;
str = s;
super();
text = str;
}
}
;
}
}
final class Prova$1
{
public String toString()
{
return (new StringBuilder()).append("Anonymous, text=").append(text).toString();
}

private String text;
final String val$str;
final Prova this$0;

Prova$1()
{
this$0 = final_prova;
val$str = String.this;
super();
text = val$str;
}
}Non ho ben capito le cose che ho segnato il rosso: da dove salta fuori quella variabile 's'?? E final_prova, String.this che cosa sono???

redcloud
03-04-2007, 17:17
Ma ora se ricompili questi due file usciti fuori da jad cosa succede?

kingv
03-04-2007, 19:21
non è un buon approccio quello che stai seguendo, jad non sempre riesce a ricavare il sorgente dal bytecode (e questo è il caso, il codice che hai postato non compila neanche).

in realtà la inner viene tradotta in una classe con un costruttore che prende in ingresso un'istanza della outer e la stringa a cui accedi (e che vengono salvate in due attributi final).

sto pensando al perchè anche la stringa debba essere marcata final, ma non capisco :confused:

andbin
03-04-2007, 21:40
non è un buon approccio quello che stai seguendo, jad non sempre riesce a ricavare il sorgente dal bytecode (e questo è il caso, il codice che hai postato non compila neanche).La mia intenzione non era/è certo quella di ricompilare i sorgenti ottenuti dalla decompilazione! Volevo solo analizzare (leggi: ficcare il naso nel codice ;) ) e vedere cosa succede ad un "livello più basso" per quanto riguarda l'uso di quella variabile final.
Se il tool jad, come dici, non è adatto, magari posso poi provare con un altro decompilatore. Molto probabilmente quelle cose che ho evidenziato (es. String.this) sono errori del jad.

in realtà la inner viene tradotta in una classe con un costruttore che prende in ingresso un'istanza della outer e la stringa a cui accedi (e che vengono salvate in due attributi final).In effetti così sembra logico, dato che il costruttore viene generato appositamente dal compilatore.

kingv
03-04-2007, 22:58
Se il tool jad, come dici, non è adatto, magari posso poi provare con un altro decompilatore. Molto probabilmente quelle cose che ho evidenziato (es. String.this) sono errori del jad.



javap -c NOmeClasse

ti permette di disassemblare il codice per vedere effettivamente come viene trasformato in bytecode il tuo sorgente.
i commenti con i parametri e le variabili ti permettono di seguire abbastanza facilmente il codice.

andbin
04-04-2007, 09:31
javap -c NOmeClasse

ti permette di disassemblare il codice per vedere effettivamente come viene trasformato in bytecode il tuo sorgente.
i commenti con i parametri e le variabili ti permettono di seguire abbastanza facilmente il codice.Ho provato con javap -c a disassemblare la inner-class. È venuto fuori questo:
Compiled from "Prova.java"
final class Prova$1 extends java.lang.Object{
Prova$1(Prova, java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #1; //Field this$0:LProva;
5: aload_0
6: aload_2
7: putfield #2; //Field val$str:Ljava/lang/String;
...
....Premetto che non conosco le istruzioni del bytecode (e non mi interessa al momento conoscerle ...) ma si vede chiaramente che il costruttore della inner-class riceve il reference della classe outer (Prova nel mio caso) e una String che è poi quella variabile final.

Interessante ... grazie per il suggerimento. ;)