Obtendo o objeto da classe externa a partir do objeto da classe interna

245

Eu tenho o seguinte código. Eu quero me apossar do objeto de classe externa usando o qual eu criei o objeto de classe interna inner. Como eu posso fazer isso?

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: Bem, alguns de vocês sugeriram modificar a classe interna adicionando um método:

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

Mas e se eu não tiver controle para modificar a classe interna, (apenas para confirmar), temos outra maneira de obter o objeto de classe externa correspondente do objeto de classe interna?

pico
fonte

Respostas:

329

Dentro da própria classe interna, você pode usar OuterClass.this. Essa expressão, que permite se referir a qualquer instância que inclua lexicamente, é descrita no JLS como Qualificadathis .

Eu não acho que exista uma maneira de obter a instância de fora do código da classe interna. Claro, você sempre pode apresentar sua própria propriedade:

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

EDIT: Por experimentação, parece que o campo que contém a referência à classe externa tem acesso no nível do pacote - pelo menos com o JDK que estou usando.

EDIT: O nome used ( this$0) é realmente válido em Java, embora o JLS desencoraje seu uso:

O $caractere deve ser usado apenas no código-fonte gerado mecanicamente ou, raramente, para acessar nomes pré-existentes em sistemas legados.

Jon Skeet
fonte
Obrigado Jon! Mas e se eu não tiver controle para modificar a classe interna (verifique minha edição).
peakit
7
@peakit: Então, até onde eu sei, você está sem sorte, a menos que use reflexão. Parece que é realmente uma violação do encapsulamento - se a classe interna não quiser lhe dizer qual é sua instância externa, você deve respeitar isso e tentar projetar de forma a não precisar dela.
9139 Jon Skeet
1
Isso ainda é válido no Java 8?
enevoado
@ misty Sim, é.
Hatefiend 12/08/19
36

OuterClass.this referencia a classe externa.

bmargulies
fonte
7
Mas apenas na / dentro da fonte da OuterClass. E eu não acho que é isso que o OP quer.
Stephen C
23

Você pode (mas não deve) usar a reflexão para o trabalho:

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

Obviamente, o nome da referência implícita não é totalmente confiável; portanto, como eu disse, você não deve :-)

Lukas Eder
fonte
2

A resposta mais geral a essa pergunta envolve variáveis ​​sombreadas e como elas são acessadas.

No exemplo a seguir (do Oracle), a variável x em 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();
    }
}

A execução deste programa imprimirá:

x=0, Test.this.x=1

Mais em: http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6

Gladclef
fonte
Não sei se esse exemplo prova o ponto da melhor maneira, porque "Test.this.x" é o mesmo que "Test.x" porque é estático, realmente não pertence ao objeto de classe que o envolve. Eu acho que seria um exemplo melhor se o código estivesse no construtor da classe Test and Test.x não estático.
sb4 04/06
0

Aqui está o exemplo:

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

se você não tem controle para modificar a classe interna, a reflexão pode ajudá-lo (mas não é recomendável). este $ 0 é uma referência na classe Inner, que informa qual instância da classe Outer foi usada para criar a instância atual da classe Inner.

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

Obviamente, o nome da referência implícita não é confiável, portanto você não deve usar reflexão para o trabalho.

Vali Zhao
fonte
'Interior estático' é uma contradição em termos.
Marquês de Lorne
-2

Foram editados em 15/06 2020

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
fonte