2010-11-16 5 views
2

Est-ce que quelqu'un sait la réponse définitive à pourquoi ce qui suit n'est pas autorisé par le compilateur java?Méthode générique Covariance - restriction valide ou supervision du compilateur?

class BaseClass { 

     public <T extends Number> T getNumber(){ 
      return null; 
     } 
} 

class SubClass extends BaseClass{ 

     @Override 
     public <T extends Integer> T getNumber(){ 
      return null; 
     } 

} 

cela provoque le compilateur de se plaindre avec:

« La méthode getNumber() de type SubClass doit passer outre une méthode superclasse »

Maintenant, quand je mets à mes collègues certains ont essayé pour expliquer que cela va causer de la confusion avec le compilateur. Cependant, comme cela a également été souligné, ce qui suit, conceptuellement similaire, est compilable.

class BaseClass<T extends Number> { 
     public T getNumber(){ 
      return null; 

     } 
} 


class SubClass<T extends Integer> extends BaseClass<T>{ 
     @Override 
     public T getNumber(){ 
      return null; 

     } 
} 

Cela peut être abusé si la sous-classe appelle la super-implémentation, mais le compilateur fournit un avertissement à cet effet. Ma seule conclusion est que c'est un oubli de compilateur de la part des gens de Sun (je ne peux pas me résoudre à dire Oracle: -S).

Quelqu'un at-il la réponse définitive à celui-ci? Supposons qu'il soit en effet permis d'ajouter plus de restrictions au paramètre de type sur une classe dérivée.

+0

Aah oui, nous aimons tous Sun mais l'Oracle? (Laissez-moi me taire maintenant) .... lol –

+2

Notez que '' n'a pas beaucoup de sens puisque 'Integer' est final. Vous pourriez par exemple le changer de 'Integer' à' BigInteger' ou quelque chose comme ça ... – aioobe

Répondre

4

Et si votre classe avait aussi une méthode <T extends Number> void setNumber(T number)?

BaseClass foo = new SubClass(); 
long longValue = 42; 
foo.<Long>setNumber(longValue); 

Le plus haut, serait accepté par le compilateur car BaseClass.setNumber accepte un paramètre de type dérivé de Number. Mais l'instance réelle n'accepte que des nombres entiers!

Vous pourriez faire valoir que si le paramètre de type est seulement utilisé pour la valeur de retour, il devrait automatiquement être considéré comme covariant. Mais alors le compilateur devrait s'assurer que vous n'utilisez pas le paramètre de type dans le corps de la méthode d'une manière non-covariante.

(En C# 4.0 this was actually solved, mais il implique marquer explicitement vos paramètres de type comme covariant ou contravariant avec les out et in mots-clés. Il était pas quelque chose qui pourrait simplement être autorisé par le compilateur sans changer la langue.)

+1

Ma pensée était aussi dans ce sens - rendre le compilateur plus sophistiqué pour permettre à l'auteur d'utiliser le langage à la limite, mais de le protéger à partir d'erreurs potentielles ... – Ellis

0

I ne pas suivre tout à fait votre problème:

Lorsque j'ai implémenté votre première partie de code source et compilé en utilisant javac *.java, il compilé très bien. Cependant, si je changé votre méthode SubClass.getNumber() pour le faire ...

/* (non-Javadoc) 
    * @see constraint.base.BaseClass#getNumber() 
    */ 
@Override 
public <T extends Number> T getNumber() { 
    // TODO Auto-generated method stub 
    return super.getNumber(); 
} 

Je reçois l'erreur suivante:

SubClass.java:18: type parameters of <T>T cannot be determined; no unique maximal instance exists for type variable T with upper bounds T,java.lang.Number 
       return super.getNumber(); 
            ^
1 error 

La raison du code ci-dessus est que, en appelant super.getNumber(), le compilateur n » t ont une entrée qui infère le type T donc il ne peut pas déterminer ce T seront retournés par super.getNumber() et donc il ne peut pas satisfaire le type de retour avec SubClass.getNumber()

Maintenant, afin d'avoir obtenu l'erreur du compilateur, "The method getNumber() of type SubClass must override a superclass method", votre BaseClass aurait dû être résumé avec sa méthode getNumber().

Quant au 2ème morceau de code que vous avez fourni, la sous-classe peut, en effet, appeler super.getNumber() comme le compilateur saura (sur la compilation) que Type de paramètre générique du SubClass est déduit du type de paramètre de BaseClass. En plus de ça, je ne savais vraiment pas ce que vous essayiez de me demander.

+0

Si je supprime l'annotation @Override, je peux compiler avec succès (win/mac jdk). Cependant, dans eclipse, l'erreur suivante est signalée: "Name clash: La méthode getNumber() de type SubClass a le même effacement que getNumber() de type BaseClass mais ne l'annule pas" – Ellis

+0

Si l'annotation @Override est présente, je reçois le erreur suivante: "La méthode getNumber() de type SubClass doit redéfinir ou implémenter une méthode supertype" – Ellis

+0

Si je supprime l'annotation @Override et initialise la méthode avec un argument de type Integer dans la sous-classe, alors elle compile bien, mais si j'instancie ensuite SubClass et essayer d'appeler une méthode compilateur renvoie l'erreur suivante: référence à getNumber est ambiguë, à la fois la méthode getNumber() dans test2.BaseClass et procédé getNumber() en correspondance test2.SubClass \t \t sc. getNumber(); Si j'initialise avec un type de numéro alors il compile bien. – Ellis

0

@Wim Coenen fourni une bonne réponse.

Voici une petite clarification.

Si ce compilerait, qu'est-ce que vous attendez est arrivé:

import java.math.BigInteger; 

class BaseClass { 

    public <T extends Number> T getNumber(T n) { 
     System.out.println("Int value: " + n.intValue()); 
     return n; 
    } 
} 

class SubClass extends BaseClass { 

    @Override 
    public <T extends BigInteger> T getNumber(T n) { 
     System.out.println("Int value: " + n.intValue()); 

     // BigInteger specific! 
     System.out.println("Bit count: " + n.bitCount()); 
     return n; 
    } 
} 

public class Main { 
    public static void main(String[] args) { 
     BaseClass sub = new SubClass(); 

     // sub looks like a BaseClass... 
     // ...but since it's a SubClass it expects a BigInteger! 
     sub.getNumber(new Integer(5)); 
    } 
} 
+0

Eh bien, vous venez de prouver exactement ce que j'ai dit avec l'exemple de code (que j'ai aussi fait, mais pas complètement posté dans mon cas). –

+0

Ok. Si vous le dites ... Je ne suis pas sûr de comprendre votre réponse. Pourquoi continuez-vous à mentionner «super» dans chaque section? – aioobe

+0

Je montrais que son code posté compilé correctement lorsqu'il est compilé en dehors d'Eclipse (puisqu'il 'renvoie null' mais en autorisant' super.getNumber() 'c'est là que les problèmes commencent –

0

Parce qu'il n'y a pas covariance ici. Covariance signifie «varie avec», et fait référence au type de retour variant en tant que type de classe conteneur. Cela ne se passe pas ici. Il n'y a pas de "co" dans votre covariance.

+0

Je pense que vous évitez la question – aioobe

+0

Le PO est simplement une mauvaise utilisation de la terminologie, c'est la source de sa confusion. – EJP

Questions connexes