Je lis sur les méthodes génériques d' OracleDocGenericMethod . Je suis assez confus au sujet de la comparaison quand il dit quand utiliser des caractères génériques et quand utiliser des méthodes génériques. Citant le document.
interface Collection<E> { public boolean containsAll(Collection<?> c); public boolean addAll(Collection<? extends E> c); }
Nous aurions pu utiliser des méthodes génériques ici à la place:
interface Collection<E> { public <T> boolean containsAll(Collection<T> c); public <T extends E> boolean addAll(Collection<T> c); // Hey, type variables can have bounds too! }
[…] Cela nous indique que l'argument type est utilisé pour le polymorphisme; son seul effet est de permettre l'utilisation d'une variété de types d'arguments réels sur différents sites d'invocation. Si tel est le cas, il faut utiliser des caractères génériques. Les caractères génériques sont conçus pour prendre en charge le sous-typage flexible, ce que nous essayons d'exprimer ici.
Ne pensons-nous pas que les caractères génériques (Collection<? extends E> c);
soutiennent également une sorte de polymorphisme? Alors pourquoi l'utilisation de méthodes génériques n'est-elle pas considérée comme bonne dans ce domaine?
Continuant de l'avant, il déclare,
Les méthodes génériques permettent d'utiliser des paramètres de type pour exprimer des dépendances entre les types d'un ou plusieurs arguments d'une méthode et / ou de son type de retour. S'il n'y a pas une telle dépendance, une méthode générique ne doit pas être utilisée.
Qu'est-ce que ça veut dire?
Ils ont présenté l'exemple
class Collections { public static <T> void copy(List<T> dest, List<? extends T> src) { ... }
[…]
Nous aurions pu écrire la signature de cette méthode d'une autre manière, sans utiliser du tout de jokers:
class Collections { public static <T, S extends T> void copy(List<T> dest, List<S> src) { ... }
Le document décourage la deuxième déclaration et encourage l'utilisation de la première syntaxe? Quelle est la différence entre la première et la deuxième déclaration? Les deux semblent faire la même chose?
Quelqu'un peut-il éclairer ce domaine.
?
. Vous pouvez le réécrire comme `public static <T1 étend le nombre, T2 étend le nombre> copie vide (List <T1> dest, List <T2> src) et dans ce cas, ce qui se passe devient évident.List
paramètre de type using.List<T super Integer>
n'est pas valide et ne compilera pas.<T extends X & Y>
-> plusieurs limites.Considérez l'exemple suivant de la programmation Java de James Gosling 4e édition ci-dessous où nous voulons fusionner 2 SinglyLinkQueue:
Les deux méthodes ci-dessus ont la même fonctionnalité. Alors, lequel est préférable? La réponse est la deuxième. Selon les propres mots de l'auteur:
"La règle générale est d'utiliser des caractères génériques lorsque vous le pouvez, car le code avec des caractères génériques est généralement plus lisible que le code avec plusieurs paramètres de type. Lorsque vous décidez si vous avez besoin d'une variable de type, demandez-vous si cette variable de type est utilisée pour relier deux paramètres ou plus, ou pour associer un type de paramètre au type de retour. Si la réponse est non, un caractère générique devrait suffire. "
Remarque: Dans le livre, seule la deuxième méthode est donnée et le nom du paramètre de type est S au lieu de «T». La première méthode n'est pas là dans le livre.
la source
Dans votre première question: cela signifie que s'il existe une relation entre le type du paramètre et le type de retour de la méthode, utilisez un générique.
Par exemple:
Ici, vous extrayez certains des T suivant un certain critère. Si T est
Long
vos méthodes retournerontLong
etCollection<Long>
; le type de retour réel dépend du type de paramètre, il est donc utile et conseillé d'utiliser des types génériques.Lorsque ce n'est pas le cas, vous pouvez utiliser des types de caractères génériques:
Dans ces deux exemples, quel que soit le type des éléments dans les collections, les types de retour seront
int
etboolean
.Dans vos exemples:
ces deux fonctions renverront un booléen quel que soit le type des éléments des collections. Dans le second cas, il est limité aux instances d'une sous-classe d'E.
Deuxième question:
Ce premier code vous permet de passer un hétérogène
List<? extends T> src
en paramètre. Cette liste peut contenir plusieurs éléments de différentes classes tant qu'ils étendent tous la classe de base T.si tu avais:
et
vous pourriez faire
D'autre part
contraint
List<S> src
d'être d'une classe particulière S qui est une sous-classe de T. La liste ne peut contenir que des éléments d'une classe (dans ce cas S) et d'aucune autre classe, même s'ils implémentent T aussi. Vous ne pourriez pas utiliser mon exemple précédent mais vous pourriez faire:la source
List<? extends Fruit> basket = new ArrayList<? extends Fruit>();
N'est pas une syntaxe valide. Vous devez instancier le ArrayList sans limites.ArrayList(Collection<? extends E> c)
. Pouvez-vous m'expliquer pourquoi vous avez dit cela?La méthode Wildcard est également générique - vous pouvez l'appeler avec une gamme de types.
La
<T>
syntaxe définit un nom de variable de type. Si une variable de type a une utilité (par exemple dans l'implémentation de méthode ou comme contrainte pour un autre type), alors il est logique de la nommer, sinon vous pouvez l'utiliser?
comme variable anonyme. Donc, ça ressemble à un raccourci.De plus, la
?
syntaxe n'est pas évitable lorsque vous déclarez un champ:la source
Je vais essayer de répondre à votre question, une par une.
Non. La raison en est que le caractère générique borné n'a pas de type de paramètre défini. C'est un inconnu. Tout ce qu'il "sait", c'est que le "confinement" est d'un type
E
(quel qu'il soit défini). Ainsi, il ne peut pas vérifier et justifier si la valeur fournie correspond au type borné.Il n'est donc pas judicieux d'avoir des comportements polymorphes sur les caractères génériques.
La première option est meilleure dans ce cas car elle
T
est toujours bornée, etsource
aura certainement des valeurs (d'inconnues) qui sous-classesT
.Donc, supposons que vous vouliez copier toute la liste de nombres, la première option sera
src
, essentiellement, peut accepterList<Double>
,List<Float>
etc. car il existe une limite supérieure au type paramétré trouvé dansdest
.La 2ème option vous obligera à lier
S
pour chaque type que vous souhaitez copier, comme ceciComme
S
c'est un type paramétré qui nécessite une liaison.J'espère que ça aide.
la source
<S extends T>
indique qu'ilS
s'agit d'un type paramétré qui est une sous-classe deT
, donc il nécessite un type paramétré (pas de caractère générique) qui est une sous-classe deT
.Une autre différence qui n'est pas répertoriée ici.
Mais ce qui suit entraînera une erreur de compilation.
la source
Pour autant que je sache, il n'y a qu'un seul cas d'utilisation où le caractère générique est strictement nécessaire (c'est-à-dire peut exprimer quelque chose que vous ne pouvez pas exprimer en utilisant des paramètres de type explicite). C'est à ce moment que vous devez spécifier une limite inférieure.
En dehors de cela, les caractères génériques servent à écrire du code plus concis, comme décrit par les instructions suivantes dans le document que vous mentionnez:
la source
Principalement -> Les caractères génériques appliquent les génériques au niveau des paramètres / arguments d'une méthode non générique. Remarque. Il peut également être effectué dans genericMethod par défaut, mais ici au lieu de? nous pouvons utiliser T lui-même.
génériques d'emballage;
SO wildcard a ses cas d'utilisation spécifiques comme celui-ci.
la source