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?

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.


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.


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 :-)


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


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

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.


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


«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 :-( ");
        }
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.