Pobieranie obiektu klasy zewnętrznej z obiektu klasy wewnętrznej

245

Mam następujący kod. Chcę zdobyć obiekt klasy zewnętrznej, za pomocą którego utworzyłem obiekt klasy wewnętrznej inner. Jak mogę to zrobić?

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 :-( ");
        }
    }
}

EDYCJA: Cóż, niektórzy z was zasugerowali zmodyfikowanie klasy wewnętrznej przez dodanie metody:

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

Ale co jeśli nie mam kontroli, aby zmodyfikować klasę wewnętrzną, to (tylko w celu potwierdzenia) mamy inny sposób na uzyskanie odpowiedniego obiektu klasy zewnętrznej z obiektu klasy wewnętrznej?

szczyt
źródło

Odpowiedzi:

329

W samej klasie wewnętrznej możesz używać OuterClass.this. To wyrażenie, które pozwala na odwołanie się do dowolnej instancji zawierającej leksykon, jest opisane w JLS jako Kwalifikowanethis .

Nie sądzę jednak, że istnieje sposób, aby uzyskać instancję spoza kodu klasy wewnętrznej. Oczywiście zawsze możesz przedstawić swoją własność:

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

EDYCJA: eksperymentalnie wygląda na to, że pole zawierające odwołanie do klasy zewnętrznej ma dostęp na poziomie pakietu - przynajmniej z JDK, którego używam.

EDYCJA: Nazwa użyta ( this$0) jest faktycznie poprawna w Javie, chociaż JLS odradza jej użycie:

$Znak powinien być stosowany tylko w wygenerowanym kodzie źródłowym mechanicznie lub, rzadziej, do dostępu do wcześniej istniejących nazw na starszych systemach.

Jon Skeet
źródło
Dzięki Jon! Ale co, jeśli nie mam kontroli, aby zmodyfikować klasę wewnętrzną (sprawdź moją edycję).
peakit
7
@peakit: W takim razie, o ile wiem, nie masz szczęścia, chyba że użyjesz refleksji. Wydaje się, że jest to jednak naruszenie enkapsulacji - jeśli klasa wewnętrzna nie chce ci powiedzieć, czym jest jej zewnętrzna instancja, powinieneś to uszanować i spróbować zaprojektować tak, aby nie była potrzebna.
Jon Skeet
1
Czy nadal obowiązuje to w Javie 8?
mglisty
@misty Tak, to prawda.
Hatefiend,
36

OuterClass.this odwołuje się do klasy zewnętrznej.

bmargulies
źródło
7
Ale tylko w / w źródle OuterClass. I nie sądzę, że tego właśnie chce OP.
Stephen C
23

Możesz (ale nie powinieneś) używać refleksji do zadania:

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);
    }
}

Oczywiście nazwa domyślnego odniesienia jest całkowicie niewiarygodna, więc jak powiedziałem, nie powinieneś :-)

Lukas Eder
źródło
2

Bardziej ogólna odpowiedź na to pytanie obejmuje zmienne zaciemnione i sposób ich dostępu.

W poniższym przykładzie (z Oracle) zmienna x w main () to shadowing 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();
    }
}

Uruchomienie tego programu spowoduje wydrukowanie:

x=0, Test.this.x=1

Więcej na: http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6

Gladclef
źródło
Nie jestem pewien, czy ten przykład najlepiej potwierdza tę kwestię, ponieważ „Test.this.x” jest taki sam jak „Test.x”, ponieważ jest statyczny, tak naprawdę nie należy do otaczającego obiektu klasy. Myślę, że byłby lepszy przykład, gdyby kod był w konstruktorze klas Test and Test.x nie statyczny.
sb4
0

Oto przykład:

// 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";
   }
}
Ashish Rawat
źródło
0

jeśli nie masz kontroli nad modyfikacją klasy wewnętrznej, analiza może ci pomóc (ale nie polecam). $ 0 to odwołanie w klasie wewnętrznej, które mówi, które wystąpienie klasy zewnętrznej zostało użyte do utworzenia bieżącego wystąpienia klasy wewnętrznej.

曹建 发
źródło
-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);
}

Oczywiście nazwa niejawnego odwołania jest niewiarygodna, dlatego nie należy używać refleksji dla zadania.

Vali Zhao
źródło
„Statyczny wewnętrzny” jest wewnętrznie sprzecznością.
Markiz Lorne
-2

Zostały zredagowane w 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 :-( ");
        }
    }
}
Kyakya
źródło