2009-10-03 8 views
5

J'ai une interface A, qui est implémentée par la classe B.Java Generics Curiosité

La méthode générique suivante fonctionne

public static <T, U extends T> List<T> listFactory(Collection<U> source) { 
    return new ArrayList<T>(source); 
} 

mais

public static <T> List<T> listFactory(Collection<? extends T> source) { 
    return new ArrayList<T>(source); 
} 

ne fonctionne pas (erreur de compilation, incompatibilité de type), quand je dirige la sortie en

List<A> tester = listFactory(B.defaultCollectionFactory(3)); 

defaultCollectionFactory(int count) statiquement fournit une collection de B s, avec un schéma d'étiquetage par défaut.

Avez-vous une idée de pourquoi? Il semble que les génériques U et wildcard font la même chose.

+1

Comment ça "ne fonctionne pas"? quel est le message d'erreur? – newacct

+1

On dirait que vous avez deux bonnes réponses ci-dessous pour ce qui cause le problème - mais je ne prendrais pas leurs conseils à la lettre. Stick avec la version non générique, de sorte que l'appelant de la méthode ne doit pas contourner cette limitation de caractères génériques. –

+0

@newacct: erreur de compilation, incompatibilité de type – Carl

Répondre

2

Dans la première construction, vous spécifiez que vous renvoyez un List de l'interface de l'élément qui a été transmis. Vous spécifiez la relation entre le passé dans Object et le type d'objet de retour dans la direction U extends T. Dans ce cas, le compilateur peut associer respectivement A et B avec T et U. Dans la seconde, il n'y a pas de telle différenciation, donc le compilateur suppose que T fait référence à B et tapera la valeur de retour List<B>. Vous tombez alors dans le piège où, bien que B soit une instance de A, List<B> n'est pas une instance de List<A>. Le compilateur se plaindra:

Incompatibilité de type: ne peut pas convertir de la liste <B> à la liste <A>

Vous constaterez que, avec la première construction, vous avez la liberté de spécifier un List de toute interface B implémente ou n'importe quelle super-classe dans la hiérarchie B (List<Object>, par exemple), et le compilateur ne se plaindra pas.

+0

Donc, la préséance (sorte de) pour l'inférence de type provient des arguments d'abord, puis du type de retour pour ce genre de génériques. – Carl

+0

choisi sur la base d'une bonne explication (qu'Erickson propose également), en plus d'éviter de conseiller la Classe. structure, que je trouve moche. – Carl

3

Le compilateur infère un paramètre de type différent pour la méthode listFactory que prévu. Il en déduit que T est de type B, donc la signature est effectivement List<B> listFactory(Collection<? extends B> source). Spécifiez le paramètre de type A en étant explicite dans l'invocation de la méthode:

List<A> tester = Test.<A> listFactory(B.defaultCollectionFactory(3)); 
+0

@erikson: Cet appel statique sur un type paramétré fonctionne réellement, n'a pas de sens pour moi, pouvez-vous le décomposer avec un exemple et une explication de la théorie, merci. –

+0

Toute méthode statique peut (vraiment, devrait) être qualifiée par la classe à laquelle elle appartient. L'OP n'a pas précisé de quoi il s'agissait; J'ai utilisé "Test". Cela vous donnera un appel de méthode statique normal: 'Test.listFactory (...)'. Ensuite, les arguments de type pour toute méthode paramétrée peuvent être spécifiés explicitement, en les ajoutant avant le nom de la méthode. C'est le ''. Mettez-les ensemble, et vous obtenez ma réponse. – erickson