2010-04-09 5 views
9

J'ai un problème lors de la compilation d'une classe générique avec une classe interne. La classe étend une classe générique, la classe interne aussi.Erreur de compilation sur l'héritage de classe interne générique s'étendant avec des limites

Voici l'interface implémentée:

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

Le super classe générique:

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

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

La sous-classe générique avec l'erreur du compilateur:

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

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

L'erreur est:

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

Qu'est-ce qui ne va pas? Le code compile avec eclipse, mais pas avec le compilateur java 5 (j'utilise ant avec java 5 sur un mac et eclipse 3.5). Non, je ne peux pas le convertir en classe interne statique.

+1

Avez-vous une chance de l'essayer avec Java 6?Ce pourrait être un bug du compilateur. –

+0

Juste essayé avec Java 6. Même message d'erreur. Intéressant ... –

+0

@mmyers: bonne idée @Eval: merci, aussi essayé moi-même sur le mac 1.6 jdk - même erreur –

Répondre

8

Le Java Language Specification, §8.1.3, définit la sémantique des sous-classement types internes comme suit :

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.

Notez que l'instance englobante est décrit seulement d'être d'une classe particulière , pas un type particulier Comme tous les cas d'une action de type générique de la même classe, le code suivant serait légal:

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> {} 
} 

Bien sûr, cela peut être utilisé pour briser le type invariant (ie la cause de pollution tas):

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

Le compilateur eclipse prend la spécification de langage Java en tant que valeur faciale et accepte le code ci-dessus sans même émettre d'avertissement "non contrôlé". Bien que techniquement conforme à la JLS, cela viole clairement son intention.

Le Sun Java compilateur rejette la déclaration de StrangeSubInner avec:

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

Apparemment, le compilateur n'a pas simplement vérifier le paramètre de type contre super paramètre de type classe intérieur de lié comme Eclipse a fait. Dans ce cas, je crois que c'est la bonne chose à faire, car la déclaration est clairement dangereuse. Cependant, le compilateur Sun rejette également la déclaration suivante, même si elle est de type démontrable sûr:

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

Mon intuition est que la vérification de la cohérence de ces restrictions de type en forme de losange est au-delà des capacités du compilateur Sun et de telles constructions sont donc rejetées sommairement à la place.

Pour contourner cette limitation, je vais d'abord essayer de se débarrasser du paramètre de type à CompoundIterator.

+0

Merci, donc je dois le faire à l'ancienne - juste cast (le générique a été utilisé pour un membre). –

+0

+1 Très intéressant. –

1

Peut-être que ce n'est pas beaucoup de progrès, mais je réussi à réduire le code ci-dessus le code suivant qui présente toujours le même comportement bizarre:

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

class Sub<E> extends Base<E>{ 
    class SubInner extends BaseInner<E> { 
    } 
} 
Questions connexes