Obtention de l'objet de classe externe à partir de l'objet de classe interne

245

J'ai le code suivant. Je veux mettre la main sur l'objet de classe externe à l'aide duquel j'ai créé l'objet de classe interne inner. Comment puis-je le faire?

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: Eh bien, certains d'entre vous ont suggéré de modifier la classe interne en ajoutant une méthode:

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

Mais que se passe-t-il si je n'ai pas le contrôle pour modifier la classe interne, alors (juste pour confirmer) avons-nous une autre façon d'obtenir l'objet de classe externe correspondant à partir de l'objet de classe interne?

Peakit
la source

Réponses:

329

Dans la classe interne elle-même, vous pouvez utiliser OuterClass.this. Cette expression, qui permet de faire référence à toute instance lexicalement englobante, est décrite dans le JLS comme qualifiéethis .

Je ne pense pas qu'il y ait un moyen d'obtenir l'instance de l'extérieur du code de la classe interne. Bien sûr, vous pouvez toujours présenter votre propre propriété:

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

EDIT: Par expérimentation, il semble que le champ contenant la référence à la classe externe ait un accès au niveau du package - au moins avec le JDK que j'utilise.

EDIT: Le nom utilisé ( this$0) est en fait valide en Java, bien que le JLS décourage son utilisation:

Le $caractère ne doit être utilisé que dans le code source généré mécaniquement ou, rarement, pour accéder aux noms préexistants sur les systèmes hérités.

Jon Skeet
la source
Merci Jon! Mais que se passe-t-il si je n'ai pas le contrôle pour modifier la classe interne (vérifiez ma modification).
Peakit
7
@peakit: Autant que je sache, vous n'avez pas de chance à moins que vous n'utilisiez la réflexion. Cela ressemble vraiment à une violation de l'encapsulation - si la classe interne ne veut pas vous dire quelle est son instance externe, vous devez respecter cela et essayer de concevoir de telle sorte que vous n'en ayez pas besoin.
Jon Skeet
1
Est-ce toujours valable dans Java 8?
misty
@misty Oui, ça l'est.
Hatefiend
36

OuterClass.this fait référence à la classe externe.

bmargulies
la source
7
Mais uniquement dans / à l'intérieur de la source de la classe externe. Et je ne pense pas que c'est ce que souhaite le PO.
Stephen C
23

Vous pouvez (mais vous ne devriez pas) utiliser la réflexion pour le travail:

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

Bien sûr, le nom de la référence implicite n'est absolument pas fiable, donc comme je l'ai dit, vous ne devriez pas :-)

Lukas Eder
la source
2

La réponse plus générale à cette question concerne les variables masquées et la manière dont elles sont accessibles.

Dans l'exemple suivant (d'Oracle), la variable x dans main () est l'observation de 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'exécution de ce programme affichera:

x=0, Test.this.x=1

Plus d'informations sur: http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6

Gladclef
la source
Pas sûr que cet exemple prouve le mieux parce que "Test.this.x" est identique à "Test.x" car il est statique, n'appartient pas vraiment à l'objet de classe englobant. Je pense que ce serait un meilleur exemple si le code était dans le constructeur de la classe Test et Test.x pas statique.
sb4
0

Voici l'exemple:

// 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
la source
0

si vous n'avez pas le contrôle pour modifier la classe interne, la refection peut vous aider (mais pas à recommander). ce $ 0 est une référence dans la classe Inner qui indique quelle instance de la classe Outer a été utilisée pour créer l'instance actuelle de la classe Inner.

曹建 发
la source
-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);
}

Bien sûr, le nom de la référence implicite n'est pas fiable, vous ne devez donc pas utiliser la réflexion pour le travail.

Vali Zhao
la source
«Intérieur statique» est une contradiction dans les termes.
Marquis de Lorne
-2

Ont été modifiés en 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
la source