Ottenere l'oggetto di classe esterno dall'oggetto di classe interno


245

Ho il codice seguente. Voglio ottenere l'oggetto di classe esterno utilizzando il quale ho creato l'oggetto di classe interno inner. Come posso farlo?

public class OuterClass {

    public class InnerClass {
        private String name = "Peakit";
    }

    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        InnerClass inner = outer.new InnerClass();
       // How to get the same outer object which created the inner object back?
        OuterClass anotherOuter = ?? ;

        if(anotherOuter == outer) {
             System.out.println("Was able to reach out to the outer object via inner !!");
        } else {
             System.out.println("No luck :-( ");
        }
    }
}

EDIT: Beh, alcuni di voi ragazzi hanno suggerito di modificare la classe interna aggiungendo un metodo:

public OuterClass outer() {
   return OuterClass.this;
}

Ma cosa succede se non ho il controllo per modificare la classe interna, quindi (solo per confermare) abbiamo qualche altro modo per ottenere l'oggetto della classe esterna corrispondente dall'oggetto della classe interna?

Risposte:


329

All'interno della stessa classe interna, puoi usare OuterClass.this. Questa espressione, che consente di fare riferimento a qualsiasi istanza che racchiude lessicalmente, è descritta nel JLS come qualificatathis .

Non penso che ci sia un modo per ottenere l'istanza al di fuori del codice della classe interna. Naturalmente, puoi sempre presentare la tua proprietà:

public OuterClass getOuter() {
    return OuterClass.this;
}

EDIT: Per sperimentazione, sembra che il campo che contiene il riferimento alla classe esterna abbia accesso a livello di pacchetto - almeno con il JDK che sto usando.

EDIT: il nome usato ( this$0) è effettivamente valido in Java, sebbene JLS scoraggia il suo utilizzo:

Il $personaggio dovrebbe essere usato solo nel codice sorgente generato meccanicamente o, raramente, per accedere a nomi preesistenti su sistemi legacy.


Grazie Jon! E se non avessi il controllo per modificare la classe interna (controlla la mia modifica).
picco

7
@peakit: Quindi, per quanto ne so, sei sfortunato a meno che non usi la riflessione. Sembra davvero una violazione dell'incapsulamento - se la classe interna non vuole dirti qual è la sua istanza esterna, dovresti rispettarla e provare a progettare in modo tale da non averne bisogno.
Jon Skeet,

1
È ancora valido in Java 8?
nebbioso

@misty Sì, lo è.
Odio il

36

OuterClass.this fa riferimento alla classe esterna.


7
Ma solo nella / all'interno della sorgente di OuterClass. E non penso che questo sia ciò che vuole l'OP.
Stephen C,

23

Potresti (ma non dovresti) usare la riflessione per il lavoro:

import java.lang.reflect.Field;

public class Outer {
    public class Inner {
    }

    public static void main(String[] args) throws Exception {

        // Create the inner instance
        Inner inner = new Outer().new Inner();

        // Get the implicit reference from the inner to the outer instance
        // ... make it accessible, as it has default visibility
        Field field = Inner.class.getDeclaredField("this$0");
        field.setAccessible(true);

        // Dereference and cast it
        Outer outer = (Outer) field.get(inner);
        System.out.println(outer);
    }
}

Naturalmente, il nome del riferimento implicito è assolutamente inaffidabile, quindi, come ho detto, non dovresti :-)


2

La risposta più generale a questa domanda riguarda le variabili ombreggiate e il modo in cui sono accessibili.

Nel seguente esempio (da Oracle), la variabile x in main () esegue l' ombreggiatura Test.x :

class Test {
    static int x = 1;
    public static void main(String[] args) {
        InnerClass innerClassInstance = new InnerClass()
        {
            public void printX()
            {
                System.out.print("x=" + x);
                System.out.println(", Test.this.x=" + Test.this.x);
            }
        }
        innerClassInstance.printX();
    }

    public abstract static class InnerClass
    {
        int x = 0;

        public InnerClass() { }

        public abstract void printX();
    }
}

L'esecuzione di questo programma stamperà:

x=0, Test.this.x=1

Maggiori informazioni su: http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6


Non sono sicuro che l'esempio sia il migliore perché "Test.this.x" è uguale a "Test.x" perché è statico, non appartiene all'oggetto classe che lo racchiude. Penso che sarebbe un esempio migliore se il codice fosse nel costruttore della classe Test e Test.x non statico.
sb4,

0

Ecco l'esempio:

// Test
public void foo() {
    C c = new C();
    A s;
    s = ((A.B)c).get();
    System.out.println(s.getR());
}

// classes
class C {}

class A {
   public class B extends C{
     A get() {return A.this;}
   }
   public String getR() {
     return "This is string";
   }
}

0

se non hai il controllo per modificare la classe interna, il refection può aiutarti (ma non consigliare). questo $ 0 è riferimento nella classe interna che indica quale istanza della classe esterna è stata utilizzata per creare l'istanza corrente della classe interna.


-1
/**
 * Not applicable to Static Inner Class (nested class)
 */
public static Object getDeclaringTopLevelClassObject(Object object) {
    if (object == null) {
        return null;
    }
    Class cls = object.getClass();
    if (cls == null) {
        return object;
    }
    Class outerCls = cls.getEnclosingClass();
    if (outerCls == null) {
        // this is top-level class
        return object;
    }
    // get outer class object
    Object outerObj = null;
    try {
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            if (field != null && field.getType() == outerCls
                    && field.getName() != null && field.getName().startsWith("this$")) {
                field.setAccessible(true);
                outerObj = field.get(object);
                break;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return getDeclaringTopLevelClassObject(outerObj);
}

Naturalmente, il nome del riferimento implicito è inaffidabile, quindi non dovresti usare la riflessione per il lavoro.


"Statico interiore" è una contraddizione in termini.
Marchese di Lorne,

-2

Sono stati modificati nel 2020-06-15

public class Outer {

    public Inner getInner(){
        return new Inner(this);
    }

    static class Inner {

        public final Outer Outer;

        public Inner(Outer outer) {
            this.Outer=outer;
        }
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        Inner inner = outer.getInner();
        Outer anotherOuter=inner.Outer;

        if(anotherOuter == outer) {
            System.out.println("Was able to reach out to the outer object via inner !!");
        } else {
            System.out.println("No luck :-( ");
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.