Abrufen des äußeren Klassenobjekts vom inneren Klassenobjekt


245

Ich habe den folgenden Code. Ich möchte das äußere Klassenobjekt erreichen, mit dem ich das innere Klassenobjekt erstellt habe inner. Wie kann ich es tun?

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: Nun, einige von euch haben vorgeschlagen, die innere Klasse durch Hinzufügen einer Methode zu modifizieren:

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

Aber was ist, wenn ich keine Kontrolle zum Ändern der inneren Klasse habe? Haben wir dann (nur zur Bestätigung) eine andere Möglichkeit, das entsprechende äußere Klassenobjekt vom inneren Klassenobjekt zu erhalten?

Antworten:


329

Innerhalb der inneren Klasse selbst können Sie verwenden OuterClass.this. Dieser Ausdruck, der es ermöglicht, auf jede lexikalisch einschließende Instanz zu verweisen, wird im JLS als Qualifiziert beschriebenthis .

Ich glaube nicht, dass es eine Möglichkeit gibt, die Instanz von außerhalb des Codes der inneren Klasse zu erhalten. Natürlich können Sie jederzeit Ihre eigene Immobilie vorstellen:

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

BEARBEITEN: Experimentell sieht es so aus, als ob das Feld, das den Verweis auf die äußere Klasse enthält, Zugriff auf Paketebene hat - zumindest mit dem JDK, das ich verwende.

BEARBEITEN: Der verwendete Name ( this$0) ist in Java tatsächlich gültig, obwohl das JLS von seiner Verwendung abrät :

Das $Zeichen sollte nur in mechanisch generiertem Quellcode oder selten für den Zugriff auf bereits vorhandene Namen auf Legacy-Systemen verwendet werden.


Danke Jon! Aber was ist, wenn ich keine Kontrolle über das Ändern der inneren Klasse habe (überprüfen Sie meine Bearbeitung).
Höhepunkt

7
@peakit: Dann, soweit ich weiß, hast du kein Glück, es sei denn, du benutzt Reflexion. Es fühlt sich jedoch so an, als wäre es eine Verletzung der Kapselung. Wenn die innere Klasse Ihnen nicht sagen möchte, was ihre äußere Instanz ist, sollten Sie dies respektieren und versuchen, so zu gestalten, dass Sie es nicht benötigen.
Jon Skeet

1
Ist das in Java 8 noch gültig?
neblig

@misty Ja, das ist es.
Hatefiend

36

OuterClass.this verweist auf die äußere Klasse.


7
Aber nur in / innerhalb der Quelle der OuterClass. Und ich denke nicht, dass das OP das will.
Stephen C

23

Sie könnten (sollten aber nicht) Reflexion für den Job verwenden:

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

Natürlich ist der Name der impliziten Referenz absolut unzuverlässig, also sollten Sie, wie gesagt, nicht :-)


2

Die allgemeinere Antwort auf diese Frage betrifft schattierte Variablen und wie auf sie zugegriffen wird.

Im folgenden Beispiel (von Oracle) beschattet die Variable x in main () 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();
    }
}

Wenn Sie dieses Programm ausführen, wird Folgendes gedruckt:

x=0, Test.this.x=1

Weitere Informationen finden Sie unter : http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6


Ich bin mir nicht sicher, ob dieses Beispiel den Punkt am besten beweist, da "Test.this.x" mit "Test.x" identisch ist, da es statisch ist und nicht wirklich zum umschließenden Klassenobjekt gehört. Ich denke, wäre ein besseres Beispiel, wenn der Code im Konstruktor der Klassen Test und Test.x nicht statisch wäre.
sb4

0

Hier ist das Beispiel:

// 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

Wenn Sie keine Kontrolle über das Ändern der inneren Klasse haben, kann die Refektion Ihnen helfen (aber nicht empfehlen). Diese $ 0 ist eine Referenz in der inneren Klasse, die angibt, welche Instanz der äußeren Klasse zum Erstellen der aktuellen Instanz der inneren Klasse verwendet wurde.


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

Natürlich ist der Name der impliziten Referenz unzuverlässig, daher sollten Sie keine Reflexion für den Job verwenden.


'Static Inner' ist ein Widerspruch.
Marquis von Lorne

-2

Wurden in 2020-06-15 bearbeitet

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 :-( ");
        }
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.