Obtener el objeto de clase externa del objeto de clase interna

245

tengo el siguiente código. Quiero obtener el objeto de clase externa usando el cual creé el objeto de clase interna inner. ¿Cómo puedo hacerlo?

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

EDITAR: Bueno, algunos de ustedes sugirieron modificar la clase interna agregando un método:

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

Pero, ¿qué pasa si no tengo control para modificar la clase interna, entonces (solo para confirmar) ¿tenemos alguna otra forma de obtener el objeto de clase externa correspondiente del objeto de clase interna?

pico
fuente

Respuestas:

329

Dentro de la clase interna, puedes usar OuterClass.this. Esta expresión, que permite referirse a cualquier instancia léxica, se describe en el JLS como Calificadathis .

Sin embargo, no creo que haya una manera de obtener la instancia desde fuera del código de la clase interna. Por supuesto, siempre puede presentar su propia propiedad:

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

EDITAR: Por experimentación, parece que el campo que contiene la referencia a la clase externa tiene acceso a nivel de paquete, al menos con el JDK que estoy usando.

EDITAR: El nombre utilizado ( this$0) es realmente válido en Java, aunque JLS desalienta su uso:

El $carácter debe usarse solo en código fuente generado mecánicamente o, rara vez, para acceder a nombres preexistentes en sistemas heredados.

Jon Skeet
fuente
Gracias Jon! Pero qué pasa si no tengo control para modificar la clase interna (verifique mi edición).
Peakit
77
@peakit: Entonces, que yo sepa, no tienes suerte a menos que uses la reflexión. Sin embargo, en realidad parece una violación de la encapsulación: si la clase interna no quiere decirle cuál es su instancia externa, debe respetar eso e intentar diseñar de tal manera que no lo necesite.
Jon Skeet el
1
¿Sigue siendo válido en Java 8?
brumoso
@ misty Sí, lo es.
Hatefiend
36

OuterClass.this hace referencia a la clase externa.

bmargulies
fuente
77
Pero solo dentro / dentro de la fuente de OuterClass. Y no creo que eso sea lo que quiere el OP.
Stephen C
23

Podrías (pero no deberías) usar la reflexión para el trabajo:

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

Por supuesto, el nombre de la referencia implícita no es confiable, por lo que, como dije, no deberías :-)

Lukas Eder
fuente
2

La respuesta más general a esta pregunta involucra variables sombreadas y cómo se accede a ellas.

En el siguiente ejemplo (de Oracle), la variable x en main () está sombreando 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();
    }
}

La ejecución de este programa imprimirá:

x=0, Test.this.x=1

Más en: http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6

Gladclef
fuente
No estoy seguro de que el ejemplo demuestre mejor el punto porque "Test.this.x" es el mismo que "Test.x" porque es estático, realmente no pertenece al objeto de clase adjunto. Creo que sería un mejor ejemplo si el código estuviera en el constructor de la clase Test y Test.x no estático.
sb4
0

Aquí está el ejemplo:

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

Si no tiene control para modificar la clase interna, el reflejo puede ayudarlo (pero no recomendarlo). Este $ 0 es una referencia en la clase interna que indica qué instancia de la clase externa se usó para crear la instancia actual de la clase interna.

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

Por supuesto, el nombre de la referencia implícita no es confiable, por lo que no debe usar la reflexión para el trabajo.

Vali Zhao
fuente
'Estático interno' es una contradicción en los términos.
Marqués de Lorne
-2

Han sido editados 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
fuente