2010-05-02 9 views
13

Existe-t-il une différence pratique entre les approches suivantes pour imprimer tous les éléments d'une plage?Caractères génériques et méthodes génériques

public static void printA(Iterable<?> range) 
{ 
    for (Object o : range) 
    { 
     System.out.println(o); 
    } 
} 

public static <T> void printB(Iterable<T> range) 
{ 
    for (T x : range) 
    { 
     System.out.println(x); 
    } 
} 

Apparemment, printB implique une somme supplémentaire à l'objet coulé contrôlée Cette (ligne 16), ce qui me semble assez stupide - n'est pas tout un objet de toute façon?

public static void printA(java.lang.Iterable); 
    Code: 
    0: aload_0 
    1: invokeinterface #18, 1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator; 
    6: astore_2 
    7: goto 24 
    10: aload_2 
    11: invokeinterface #24, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 
    16: astore_1 
    17: getstatic #30; //Field java/lang/System.out:Ljava/io/PrintStream; 
    20: aload_1 
    21: invokevirtual #36; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
    24: aload_2 
    25: invokeinterface #42, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z 
    30: ifne 10 
    33: return 

public static void printB(java.lang.Iterable); 
    Code: 
    0: aload_0 
    1: invokeinterface #18, 1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator; 
    6: astore_2 
    7: goto 27 
    10: aload_2 
    11: invokeinterface #24, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 
    16: checkcast #3; //class java/lang/Object 
    19: astore_1 
    20: getstatic #30; //Field java/lang/System.out:Ljava/io/PrintStream; 
    23: aload_1 
    24: invokevirtual #36; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
    27: aload_2 
    28: invokeinterface #42, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z 
    33: ifne 10 
    36: return 

Répondre

7

Dans votre exemple, le type générique est utilisé dans exactement un endroit de la signature. Dans ce scénario, un type T n'a aucun avantage sur un caractère générique pour l'appelant. Dans votre exemple, le type n'a également aucun avantage pour l'implémenteur de la méthode.

Je trouve la version générique plus facile à comprendre pour un appelant, car il dit explicitement "Je ne me soucie pas du type du tout".

Dans votre exemple, le checkcast est en effet superflu. Il serait nécessaire si T était borné, comme dans T extends Number. Une checkcast pour Number est requise, car la variable locale x sera de type Number, mais la méthode Iterator.next() renvoie toujours Object. On dirait que le compilateur Java ne se soucie pas d'optimiser la distribution. Le JIT le fera probablement à l'exécution.

MISE À JOUR:

Si le type générique est utilisé dans plusieurs endroits, comme dans la réponse de Cletus, vous n'avez pas d'autre choix que d'utiliser un type générique T, ou bien le compilateur ne voit aucune connexion entre le type de paramètre/return type (deux caractères génériques sont différents pour le compilateur). Un cas limite est lorsque la signature a seulement le type en un point, mais l'implémentation doit être un type générique plutôt qu'un caractère générique. Pensez à une méthode void swap(List<T> list, int a, int b). Il doit retirer les éléments de la liste et les remettre en place. IIRC, Effective Java suggère d'utiliser une méthode publique avec le caractère générique et une méthode d'assistance interne avec un type contenant l'implémentation réelle. De cette façon, l'utilisateur obtient une API simple, et l'implémenteur a toujours la sécurité de type.

public void swap(List<?> list, int a, int b){ 
    swapHelper(list, a, b); 
} 
private <T> void swapHelper(List<T> list, int a, int b){ 
    ... 
} 
+0

Je suis d'accord: * dans ce cas * la version générique est meilleure. – Jorn

2

La seconde est plus flexible. Un meilleur exemple est: Cela dit quelque chose sur le type. Que cela vous soit utile ou non dépend de ce que fait la fonction.

La seconde montre son utilité lorsque vous voulez retourner quelque chose de la méthode:

public static <T> List<T> reverse(List<T> list) { 
    for (int i=0; i<n/2; i++) { 
    T value = list.get(i); 
    list.set(i, list.get(list.size() - i - 1)); 
    list.set(list.size() - i = 1, value); 
    } 
    return list; 
} 

Il est peut-être un meilleur exemple pour copier le tableau, mais le point reste que vous ne pouvez pas vraiment faire ce qui précède avec List<?> .

+0

Je ne comprends pas cela. Autant que je sache, les deux formes peuvent être utilisées exactement dans les mêmes contextes et on ne donne pas un iota plus ou moins de sécurité de type que l'autre. Est-ce que je manque quelque chose? –

+1

@Marcelo Si vous utilisez le?version la méthode appelante obtiendrait une liste, et besoin de jeter cette liste à une liste lui-même. Ce qui est un peu peu sûr car le compilateur ne peut pas vérifier que l'implémentation produit effectivement une liste . Le contrat (avec?) Indique qu'une liste doit être renvoyée et que c'est vrai, même si ce n'est pas une liste . La version T permettrait donc au compilateur de vérifier le type de retour et la méthode d'appel serait plus sûre. – extraneon

+0

Ouais mais chaque fois que le T apparaît juste à un endroit dans la liste des arguments (comme dans l'exemple de l'OP), il peut être converti en un caractère générique. Cet exemple est différent (le T apparaît à deux endroits, y compris dans le retour). – newacct

0

Pas vraiment. Le bytecode résultant devrait être pratiquement identique.

+0

Ce n'est pas, voir mon édition. – fredoverflow

+0

Ah, oui. La version '' vérifie donc que le contenu est du type correct et peut donc échouer si un' Iterable 'contient quelque chose qui n'est pas un' T'. J'ai adouci ma réponse un peu. –

+0

@Marcello: Non, il ne vérifie pas si un Iterable contient quelque chose qui n'est pas un T. Il vérifie simplement s'il contient quelque chose qui ne peut pas être converti en Object. Mais les génériques Java sont si bêtement implémentés que je suppose que c'est juste un autre restes ... – Foxfire

Questions connexes