2010-04-09 13 views
9

Tengo un problema al compilar una clase genérica con una clase interna. La clase extiende una clase genérica, la clase interna también.Error de compilación en la herencia de la clase interna genérica que se extiende con los límites

Aquí la interfaz implementada:

public interface IndexIterator<Element> 
    extends Iterator<Element> 
{ 
    ... 
} 

El super clase genérica:

public abstract class CompoundCollection<Element, Part extends Collection<Element>> 
    implements Collection<Element> 
{ 
    ... 

    protected class CompoundIterator<Iter extends Iterator<Element>> 
     implements Iterator<Element> 
    { 
    ... 
    } 
} 

La subclase genérico con el error del compilador:

public class CompoundList<Element> 
    extends CompoundCollection<Element, List<Element>> 
    implements List<Element> 
{ 
    ... 

    private class CompoundIndexIterator 
     extends CompoundIterator<IndexIterator<Element>> 
     implements IndexIterator<Element> 
    { 
    ... 
    } 
} 

El error es:

type parameter diergo.collect.IndexIterator<Element> is not within its bound 
     extends CompoundIterator<IndexIterator<Element>> 
              ^

¿Qué pasa? El código se compila con eclipse, pero no con el compilador java 5 (utilizo hormiga con java 5 en mac y eclipse 3.5). No, no puedo convertirlo a una clase interna estática.

+1

¿Alguna posibilidad de probar con Java 6?Puede ser un error del compilador. –

+0

Acabo de probarlo con Java 6. El mismo mensaje de error. Interesante ... –

+0

@mmyers: buena idea @Eval: gracias, también me probé en el Mac 1.6 jdk - mismo error –

Respuesta

8

El Java Language Specification, §8.1.3, define la semántica de la subclasificación de los tipos internos de la siguiente :

Furthermore, for every superclass S of C which is itself a direct inner class of a class SO, there is an instance of SO associated with i, known as the immediately enclosing instance of i with respect to S. The immediately enclosing instance of an object with respect to its class' direct superclass, if any, is determined when the superclass constructor is invoked via an explicit constructor invocation statement.

Nota que la instancia que encierra solamente se describe que es de un particular, clase, no un determinado tipo. Como todas las instancias de un recurso compartido de tipo genérico de la misma clase, el código siguiente sería legal:

class Base<E> { 
    E e; 

    protected class BaseInner<I extends E>{ 
     E e() { return e; } 
    } 
} 

class StrangeSub extends Base<Integer> { 
    protected class StrangeSubInner extends Base<String>.BaseInner<String> {} 
} 

Por supuesto, esto se puede utilizar para romper el tipo invariante (es decir, causa la contaminación montón):

StrangeSub ss = new StrangeSub(); 
    ss.e = 42; 
    String s = ss.new StrangeSubInner().e(); 

El compilador de eclipse toma la especificación de idioma de Java como valor nominal, y acepta el código anterior sin siquiera emitir una advertencia de "desmarcado". Aunque podría decirse que es técnicamente compatible con el JLS, esto claramente infringe su intención.

El compilador Java de Sun rechaza la declaración de StrangeSubInner con:

Test.java:32: type parameter java.lang.String is not within its bound 
     protected class StrangeSubInner extends Base<String>.BaseInner<String> {} 
                    ^

Al parecer, el compilador no se limitó a comprobar el parámetro de tipo de parámetro de tipo de clase contra el super interno con destino al igual que lo hizo el eclipse. En este caso, creo que esto es lo correcto, ya que la declaración es claramente insegura. Sin embargo, el compilador de Sun rechaza igualmente la declaración siguiente, a pesar de que es el tipo probadamente segura:

class StrangeSub extends Base<Integer> { 
    protected class StrangeSubInner extends BaseInner<Integer> {} 
} 

Mi impresión es que la verificación de la consistencia de esta restricción de tipo en forma de diamante está más allá de las capacidades del compilador de Sun, y tales constructos son por lo tanto rechazados sumariamente.

Para evitar esta limitación, primero trataría de eliminar el parámetro tipo a CompoundIterator.

+0

Gracias, así que tengo que hacerlo a la vieja usanza - simplemente elije (el genérico se usó para un miembro) –

+0

+1 Muy interesante. –

1

Tal vez esto no es un gran progreso, pero me las arreglé para reducir el código anterior para el siguiente código que aún presenta el mismo comportamiento extraño:

class Base<E> { 
    protected class BaseInner<I extends E>{ 
    } 
} 

class Sub<E> extends Base<E>{ 
    class SubInner extends BaseInner<E> { 
    } 
} 
Cuestiones relacionadas