Domanda

Quando si passa un oggetto finale (una stringa nel codice seguente) viene visualizzato come nullo quando viene stampato dalla classe interna anonima. Tuttavia, quando viene passato un tipo di valore finale o una stringa finale diritta, il suo valore viene visualizzato correttamente. Cosa significa veramente final nel contesto della classe interna anonima e perché l'oggetto viene passato null?

public class WeirdInners
{
    public class InnerThing
    {
        public InnerThing()
        {
            print();
        }

        public void print(){

        }
    }

    public WeirdInners()
    {
        final String aString = "argh!".toString();
        final String bString = "argh!";
        System.out.println(aString);
        System.out.println(bString);


        InnerThing inner =new InnerThing(){
            public void print()
            {
                System.out.println("inner"+aString); // When called from constructor, this value is null.
                System.out.println("inner"+bString); // This value is correctly printed.
            }
        };

        inner.print();
    }


    public static void main(String[] args)
    {
        WeirdInners test1 = new WeirdInners();
    }

}

Questo è un comportamento molto strano per me perché l'aspettativa è che la stringa sia un oggetto, perché chiamare toString () cambia le cose?

Altre informazioni: questo comportamento si osserva solo utilizzando Java 1.4, non in Java 5. Qualche suggerimento su una soluzione alternativa? Non chiamare toString () su una stringa esistente è abbastanza equo, ma poiché questo è solo un esempio, ha implicazioni nel mondo reale se lo eseguo su un oggetto non String.

È stato utile?

Soluzione

Se controlli la sezione su costanti del tempo di compilazione in JLS, vedrai che chiamare .toString () fa la differenza. Come le sciocchezze come il prefisso con false? Null + " " ;: .

Ciò che è importante qui è l'ordinamento relativo dell'impostazione dei campi chiusi e del costruttore. Se si utilizza -target 1.4 o versione successiva (che non è l'impostazione predefinita in 1.4!), I campi verranno copiati prima di chiamare il super. Con le specifiche precedenti alla 1.3 questo era un bytecode illegale.

Come spesso accade in questi casi, javap -c è utile per vedere cosa sta facendo il compilatore javac. Le specifiche sono utili per capire perché (dovresti avere sufficiente pazienza).

Altri suggerimenti

La mia ipotesi sarebbe quella di innescare comportamenti indefiniti quando il costruttore di InnerThing () passa (implicitamente) il suo this al metodo di stampa di una sottoclasse anonima di InnerThing mentre l'oggetto non è completamente costruito. Questo this si basa a sua volta su un riferimento implicito al this di WierdInners.

La chiamata di .toString () sposta l'inizializzazione di aString dal tempo di compilazione al runtime. Perché il comportamento indefinito differisca tra Java 1.4 e 1.5 è probabilmente un dettaglio dell'implementazione di JVM.

È pericoloso invocare metodi sovrascritti da un costruttore di superclassi, poiché verranno chiamati prima che la parte della sottoclasse dell'oggetto sia inizializzata.

Inoltre, una classe interna che accede alle variabili finali di un ambito che racchiude effettivamente accederà alle copie di tali variabili (ecco perché devono essere definitive) e questi campi copiati risiedono nella sottoclasse anonima.

Sospetto che il motivo per cui bString sia trattato in modo diverso è che il suo valore è noto al momento della compilazione, il che consente al compilatore di incorporare l'accesso al campo nella sottoclasse, rendendo irrilevante il tempo di inizializzazione del campo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top