2009-04-18 15 views
32

Si tengo una instancia de una clase interna, ¿cómo puedo acceder a la clase externa del código que no está en la clase interna? Sé que dentro de la clase interna, puedo usar Outer.this para obtener la clase externa, pero no puedo encontrar ninguna forma externa de obtener esto.En Java, ¿cómo accedo a la clase externa cuando no estoy en la clase interna?

Por ejemplo:

public class Outer { 
    public static void foo(Inner inner) { 
    //Question: How could I write the following line without 
    // having to create the getOuter() method? 
    System.out.println("The outer class is: " + inner.getOuter()); 
    } 
    public class Inner { 
    public Outer getOuter() { return Outer.this; } 
    } 
} 
+0

podría explicar el problema que está resolviendo? ¿O es esto académico? –

+0

Académico, principalmente. Lo encontré al encontrar una respuesta a esto: http://stackoverflow.com/questions/763359/validating-instances-of-inner-classes/763504 Di dos respuestas, una haciendo lo que el OP estaba pidiendo usando la misma solución que uso arriba (un método getOuter()). Pero en mi otra respuesta (la que se ha votado a favor) le dije que la diseñara de una manera diferente para que esto no fuera necesario. Pero todavía tenía curiosidad de si era posible hacer esto. – Kip

+1

Su ejemplo proporcionado es la mejor respuesta a esta pregunta. – Pindatjuh

Respuesta

32

El bytecode de la clase Outer$Inner contendrá un campo de ámbito de paquete llamado this$0 del tipo Outer. Así es como las clases internas no estáticas se implementan en Java, porque en el nivel de bytecode no existe el concepto de una clase interna.

Debería poder leer ese campo usando la reflexión, si realmente lo desea. Nunca tuve la necesidad de hacerlo, por lo que sería mejor que cambie el diseño para que no sea necesario.

Así es como se vería su código de ejemplo al usar la reflexión. Hombre, eso es feo. ;)

public class Outer { 
    public static void foo(Inner inner) { 
     try { 
      Field this$0 = inner.getClass().getDeclaredField("this$0"); 
      Outer outer = (Outer) this$0.get(inner); 
      System.out.println("The outer class is: " + outer); 

     } catch (NoSuchFieldException e) { 
      throw new RuntimeException(e); 
     } catch (IllegalAccessException e) { 
      throw new RuntimeException(e); 
     } 
    } 

    public class Inner { 
    } 

    public void callFoo() { 
     // The constructor of Inner must be called in 
     // non-static context, inside Outer. 
     foo(new Inner()); 
    } 

    public static void main(String[] args) { 
     new Outer().callFoo(); 
    } 
} 
+2

Depende del compilador (IIRC), y necesitará un setAccessible en general. –

+0

Creo (demasiado vago para buscar las especificaciones) incluso el nombre de ese campo depende del compilador, por lo que * posiblemente * podría tener problemas si intentas hackear con otro compilador. –

+0

Estoy leyendo Java Concurrency in Practice, y uno de los ejemplos mostró cómo la referencia puede escapar con la clase interna, si pasamos una instancia de la clase interna a algún código alienígena en el constructor. Y estoy tratando de escribir el código alienígena que realmente puede atrapar la referencia antes de que el constructor regrese. Muchas gracias por la respuesta. –

14

No hay manera, por diseño. Si necesita acceder a la clase externa a través de una instancia de la interna, entonces su diseño está al revés: el punto de las clases internas generalmente se usará solo dentro de la clase externa, o a través de una interfaz.

+2

Ese también fue mi pensamiento, pero parece que no puedo encontrar ninguna documentación que aborde esta limitación. Si puede encontrar algo oficial, aceptaré gustosamente esta respuesta. – Kip

+0

No es una "limitación" explícita: ¿por qué debería haber documentación sobre la AUSENCIA de una característica en particular (bastante oscura)? –

+2

15.8.3 y 15.8.4 lo cubre. "Se puede hacer referencia a cualquier instancia léxicamente adjunta calificando explícitamente la palabra clave this". En el caso de la pregunta, no es una instancia léxicamente envolvente. –

8

¿Qué hay de malo en agregar un getter cuando necesita acceder a la clase externa? De esta forma puede controlar si el acceso está permitido o no.

0

no puedes hacer algo como esto:

public static class Inner { // static now 
    private final Outer parent; 

    public Inner(Outer parent) { this.parent = parent; } 

    public Outer get Outer() { return parent; } 
} 
+3

Quitar estática, y usar: Exterior getOuter() {return Outer.this; } –

1

Si usted tiene un (no estático) clase interna, entonces es por definición el trabajo con algo que sólo funciona dentro de la envolvente contexto de la clase externa. Entonces, para manejar la clase interna, ya tendrías que haberla recuperado a través de la instancia externa. Por lo tanto, la única forma en que puedo ver llegar al punto en el que necesitarías un acceso como ese es haber agarrado el interior a través de la referencia externa y luego haber perdido o descartado la referencia externa de la instancia.

Por ejemplo:

public class Outer{ 
    public class Inner { 
    } 
} 

public class UsesInner{ 
Collection<Outer.Inner> c = new ArrayList<Outer.Inner>(); 
} 

Ahora la única manera puede rellenar c es mediante la creación de instancias externas(). Me gustaría ver un propósito útil para algo como esto.

+0

Honestamente, tampoco puedo pensar en una buena razón.Acabo de encontrarlo tratando de responder a esta pregunta: http://stackoverflow.com/questions/763359/validating-instances-of-inner-classes/763504 En ese caso, quería asegurarse de que tenía una clase interna de instancia específica de la clase externa. Sugerí en otra respuesta que solo debería definir el método en la clase interna para que no sea un problema. Pero, una vez más, tenía curiosidad si había manera de hacerlo. – Kip

1

Aquí hay una razón por la que es posible que desee este comportamiento: Tiene la instancia de clase interna y necesita acceder a un método definido en la clase externa mediante el reflejo.

Para el registro, "inner.getClass(). GetDeclaredField (" this $ 0 ") funciona. Como el campo es privado, también debe llamar a field.setAccessible (true).

6

Esto, de hecho, es una muy buena pregunta si, por ejemplo, necesita poder verificar si dos instancias distintas de una clase Innner comparten la misma instancia de Outer clase (== o igual dependiendo del contexto)

yo sugeriría hacer una interfaz de propósito general (no absolutamente necesario para las clases internas nombradas pero puede ser "instancedof"/fundido a):

public interface InnerClass<Outer> { 
    Outer getOuter(); 
} 

que se puede aplicar a cualquier clase interna llamada .

continuación, hacer algo como:

class MyInnerClass implements InnerClass<Outer> { 
    Outer getOuter() { 
     return Outer.this; 
    } 
    // remaining implementation details 
} 

y de esta manera se puede acceder a la clase externa de cualquier clase interna implementar InnerClass<Outer> interfaz (y comprobar que en realidad lo implementa).

Si su clase interna es anónima, sólo se puede hacer (gracias a Rich MacDonald para su muestra):

new InterfaceOrAbstractClass<Outer>() { 
    Outer getOuter() { // super inefficient but this is the only way ! 
     return (Outer)getClass().getDeclaredField("this$0"); 
    } 
    /* other methods */ 
} 

pero InterfaceOrAbstractClassnecesidad implementa InnerClass<Outer> para poder acceder a la getOuter() fuera del anonimato cuerpo de clase!

Sería mucho más fácil si javac implementara automáticamente algún tipo de interfaz InnerClass<Outer> en TODAS las clases internas, y podría hacerlo de manera súper eficiente incluso en clases anónimas (¡sin procesamiento de introspección lento)!

1

¿Dónde está el problema simplemente escriba esto?

class OuterClass{ 
class InnerClass{ 
    OuterClass outerClass = OuterClass.this; 
} 

}

+4

La forma en que está escrito, es difícil decir si es una respuesta o una pregunta nueva. – Eric

Cuestiones relacionadas