2011-04-16 18 views
8

¿Cómo puedo hacer algo como este trabajo:¿Cómo hacer que una clase externa sea heredada de una clase interna?

class Outer { 
    int some_member; 

    abstract class InnerBase { 
    abstract void method(); 
    } 
} 

class OuterExtendsInner extends Outer.InnerBase { 
    OuterExtendsInner(Outer o) { o.super(); } 
    void method() { 
    // How do I use some_member here? 
    // Writing Outer.this.some_member -> error about Outer not being an enclosing class 
    // Writing just some_member -> no-go, either 
    } 
} 

La solución es tener un método en el que devuelve InnerBase Outer.this y llamar a eso de las clases derivadas, pero hay otra manera?

que principalmente desea extender el InnerBase desde el exterior con el fin de tener un mejor código-organización, pero podía mover todas las clases derivadas en exterior.

+3

"Mejor organización de código" rara vez implica clases internas, y no puedo imaginar que alguna vez involucre algo como esto. – Anon

+0

Es por eso que quería extender la clase interna fuera de la clase externa. Estoy implementando un intérprete, y cada clase "interna" modela un tipo (nombre, número entero, etc.). El comportamiento del tipo puede necesitar o no una referencia al objeto del intérprete que lo creó. He repasado el problema pasando una referencia de intérprete a cada método que pueda necesitarlo. – zvrba

Respuesta

3

El problema aquí es que el campo sintético que vincula InnerBase a Outer es un campo privado. Por lo tanto, solo podemos acceder al objeto externo desde InnerBase, o si algún método o campo proporciona una referencia al mismo objeto.

+1

+1: ¡Gracias por ayudar a aclarar esta rareza! –

3

Usted puede hacer esto en OuterExtendsInner:

class OuterExtendsInner extends Outer.InnerBase { 
    Outer o; 

    OuterExtendsInner(Outer o) { 
     o.super(); 
     this.o = o; 
    } 

    void method() { 
     // now you can reference o.some_member 
     int x = o.some_member; 
    } 
} 
+0

Eso es lo que ya sugerí en la pregunta (aunque devolver la referencia a Outer.this desde un método en InnerBase). – zvrba

+0

Vaya, he actualizado la respuesta de otra manera. – WhiteFang34

+0

Además, habría dos instancias de Outer.InnerBase en OuterExtendsInner. El heredado y el recibido en el constructor. No estarías hablando del mismo 'algún_miembro'. – JVerstry

0

Bueno el problema es que cada instancia de InnerBase (Sé que es abstracto) tiene que tener una referencia a un objeto Outer. Eso es parte de la semántica de las clases anidadas. La instancia de OuterExtendsInner necesitaría tal referencia. Puede evitar eso haciendo que InnerBase sea una clase anidada static.

+0

Este no es el problema, el problema es cómo obtener una referencia a este objeto 'Exterior'. –

1

La respuesta es: no se puede, ya que rompería la encapsulación. Solo InnerBase puede tener acceso a atributos de Outer, no de OuterExtendsInner. No es herencia directa. InnerBase no hereda de Outer.

+0

Bueno, puedes, porque los atributos de Outer no son realmente atributos de InnerBase, simplemente miran sintácticamente como si estuvieran (al menos, así es como solía ser, no me puedo imaginar que eso hubiera cambiado). Además, este problema no implica necesariamente ningún acceso directo a los atributos privados de Outer. –

+0

Pero OuterExtendsInner hereda de InnerBase, por lo que debe tener acceso a todo lo que tiene acceso a InnerBase. – zvrba

+0

@Robin Green Proporcione un ejemplo operativo, pero verá que no es posible. OuterExtendsInner no puede acceder a los atributos de Outer. – JVerstry

1

No he probado la respuesta de WhiteFang34. Podría funcionar, pero no lo tengo claro ...

Si realmente desea definir una extensión de su clase interna en otro lugar que en la clase externa, lo más natural sería definirlo como una extensión de la clase interna en otra ampliación de su clase externa externa de la siguiente manera:

class Outer { 
    int some_member; 

    abstract class InnerBase { 
    abstract void method(); 
    } 
} 

class OuterExtendsOuter extends Outer { 
    class InnerExtendsInner extends Outer.InnerBase { 
    void method() { 
     System.out.println(some_member); 
    } 
    } 
} 

que en realidad no ha ejecutado este código sea, pero debería funcionar.

Actualización:

Basado en el hilo de comentarios, ahora han compilado y ejecutado tanto mi código y el código de WhiteFang34.

Tanto en el trabajo hecho, pero como se ha señalado en los comentarios de Paulo Ebermann, tanto para crear dos copias del exterior dentro de la clase interna instancia.

voy a upvote respuesta de Paulo, y estaría a favor de no tratar de hacerlo por cualquiera de táctica, ya que es realmente un abuso del mecanismo de clase interna.

Simplemente haga su clases internas extendidas viven dentro de la misma clase externa!

Actualización 2:

lo que sucede en mi código, basado en el examen de tiempo de ejecución utilizando un depurador y en el examen de la salida de las inspecciones javap de las clases, es que tanto InnerBase y OuterExtendsOuter$InnerExtendsInner tienen campos final privado sintéticas nombradas this$0. Debido a que no hay constructores se definen explícitamente, se utilizan los constructores por defecto, y el fragmento de código

OuterExtendsOuter outer = new OuterExtendsOuter(); 
    Outer.InnerBase inner = outer.new InnerExtendsInner(); 

hace que estos dos campos a la vez referencia outer.

En otras palabras, el comentario de Paŭlo es completamente correcto.

Por la experimentación, la misma que realmente sucede si extiende InnerBase en otra clase interna de Outer, por lo que tiene poco que ver con lo que se define de la misma clase externa o una extensión de la misma, sino que es, de hecho, un resultado de cómo se manejan generalmente las clases internas no estáticas.

Sospecho que esto está documentado en alguna parte, pero no he visto eso.

¡Probablemente lo mejor es mezclar la herencia y las clases internas lo menos posible!

+0

Este objeto 'InnerExtendsInner' tiene dos enlaces para adjuntar clases (una para' InnerExtendsInner' y otra para 'InnerBase'), que por casualidad (por ejemplo, por el constructor predeterminado) apuntan al mismo objeto. –

+0

@ Paŭlo Ebermann: No te sigo. Tal vez tendré que intentar realmente compilar y ejecutar el código ... –

+0

Creo que funcionará, pero de hecho es similar a la respuesta de WhiteFang con una referencia 'Outer' explícita. (Debería hacer una imagen, pero no tengo suficiente espacio en este comentario.) –

1

¿Tiene un método getter en el InnerBase?

class Outer { 
    int some_member; 

    abstract class InnerBase { 
    abstract void method(); 
    protected int getSome_Member() // This is possible, because Abstract classes can have non-abstract methods. 
    { 
     return some_member; 
    } 
    } 
} 

class OuterExtendsInner extends Outer.InnerBase { 
    OuterExtendsInner(Outer o) { o.super(); } 
    void method() { 
     // you can access "some_member" now 
     int myNumber = getSome_Member(); 

    } 
} 
0

La clase externa puede ampliar la clase interna si la clase interna se compila en ".class".

Ahora, cada vez que se compila la clase externa se encuentra con el "extiende innerclass", que es

aún no compilado y el compilador arrojará un NoClassDefException o ClassNotFoundException.

¿No es así? Entonces nunca obtendrás esa clase interna compilada. Si puede resolver este problema

, entonces también puede ampliar la clase interna :).

Cuestiones relacionadas