Lors de l'utilisation de méthodes génériques et quand utiliser la wild-card?
Je lis à propos de méthodes génériques de OracleDocGenericMethod. Je suis assez confus au sujet de la comparaison quand il dit lors de l'utilisation de wild-card 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:
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 le type d'argument est utilisé pour le polymorphisme;
son seul effet est de permettre à une variété de réel argument de types d'
utilisé à différents invocation sites. Si c'est le cas, on devrait
utiliser des caractères génériques. Les caractères génériques sont conçus pour soutenir souple de sous-typage,
qui est ce que nous essayons d'exprimer ici.
Ne pensons-nous pas à une wild-card comme (Collection<? extends E> c);
soutient aussi le genre de
le polymorphisme? Pourquoi, alors, le générique de la méthode d'utilisation est considéré comme pas bon dans ce domaine?
De poursuivre plus en avant, il affirme, en
Méthodes génériques permettent de paramètres de type à être utilisé pour exprimer
les dépendances entre les types d'un ou de plusieurs arguments pour une méthode
et/ou son type de retour. Si il n'y a pas une dépendance, un générique
la méthode ne doit pas être utilisé.
Qu'est-ce que cela signifie?
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 l'aide de caractères génériques à tous: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 favorise l'utilisation de la première syntaxe? Quelle est la différence entre la première et la deuxième déclaration? Les deux semble être en train de faire la même chose?
Quelqu'un peut-il mettre de la lumière sur cette zone.
Vous devez vous connecter pour publier un commentaire.
Il ya certains endroits, où les caractères génériques, paramètres de type et de faire la même chose. Mais il y a aussi certains endroits, où vous devez utiliser les paramètres de type.
De prendre votre méthode à titre d'exemple, supposons que vous voulez vous assurer que le
src
etdest
liste transmise àcopy()
méthode doit être de même type paramétré, vous pouvez le faire avec des paramètres de type de la sorte:Ici, vous êtes assuré que les deux
dest
etsrc
ont le même type paramétré pourList
. Donc, il est sûr de copier des éléments d'src
àdest
.Mais, si vous passez à changer la méthode à utiliser générique:
il ne fonctionne pas comme prévu. Dans le 2ème cas, vous pouvez passer
List<Integer>
etList<Float>
commedest
etsrc
. Ainsi, en déplaçant les éléments desrc
àdest
ne serait pas le type est plus sûr.Si vous n'avez pas besoin de ce genre de relation, alors vous êtes libre de ne pas utiliser les paramètres de type à tous.
Une autre différence entre l'utilisation de caractères génériques et le type des paramètres sont:
Jokers soutenir à la fois les limites supérieures et inférieures, les paramètres de type, en plus de soutenir les limites supérieures. Donc, si vous voulez définir une méthode qui prend un
List
de typeInteger
ou c'est super classe, vous pouvez faire:mais vous ne pouvez pas utiliser les paramètres de type:
Références:
?
à tous. Vous pouvez réécrire comme " public static <T1 s'étend Nombre, T2 s'étend Nombre> void copie(List<T1> dest, List<T2> src) et dans ce cas, il devient évident que ce qui se passe.List
à l'aide de paramètre de type.List<T super Integer>
n'est pas valide et ne sera pas compilé.Envisager de suivre l'exemple de La Programmation Java par James Gosling 4ème édition ci-dessous, où nous voulons fusionner 2 SinglyLinkQueue:
Les deux méthodes ont la même fonctionnalité. Donc, ce qui est préférable? La réponse est 2e. Dans les mots de l'auteur :
"La règle générale est d'utiliser des caractères génériques quand vous le pouvez parce que code avec des caractères génériques
est généralement plus lisible que le code avec plusieurs paramètres de type. Au moment de décider si vous avez besoin d'un type de
variable, demandez-vous si ce type de variable est utilisé pour relier deux ou plusieurs paramètres, ou lier un paramètre
type avec le type de retour. Si la réponse est non, alors un caractère générique devrait suffire."
Remarque: Dans le livre seulement la deuxième méthode est donnée et le type de nom de paramètre est S au lieu de "T". La première méthode n'est pas là dans le livre.
À votre première question: Cela signifie que si il y a une relation entre le paramètre du type et de la méthode de retour de ce type, alors l'utilisation d'un générique.
Par exemple:
Ici, vous êtes à l'extraction de certains de la T à la suite d'un certain nombre de critères. Si T est
Long
vos méthodes sera de retourLong
etCollection<Long>
; le type de retour est dépendante du type de paramètre, il est donc utile, et conseillé, de l'utilisation des types génériques.Quand ce n'est pas le cas, vous pouvez utiliser les types de cartes:
Dans cet exemple deux quel que soit le type des éléments dans les collections les types de retour sera
int
etboolean
.Dans votre exemple:
ces deux fonctions retournent un booléen quel que soit les types des objets dans les collections. Dans le second cas, elle est limitée à des instances d'une sous-classe de E.
Deuxième question:
Ce premier code vous permettent de passer d'une hétérogènes
List<? extends T> src
en tant que paramètre. Cette liste peut contenir plusieurs éléments de différentes classes aussi longtemps que ils ont tous étend la classe de base T.si vous avez eu:
et
vous pourriez faire
D'autre part
contraindre
List<S> src
à être de un particulier de la classe 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 aucune autre classe, même s'ils mettent en œuvre des T trop. Vous ne seriez pas en mesure d'utiliser mon exemple précédent, mais vous pouvez faire:List<? extends Fruit> basket = new ArrayList<? extends Fruit>();
N'est pas une syntaxe valide. Vous devez instancier la liste de tableaux sans limites.ArrayList(Collection<? extends E> c)
. Pouvez-vous m'expliquer pourquoi vous avez dit que?Générique de la méthode est également générique - vous pouvez l'appeler avec une gamme de types.
La
<T>
syntaxe définit une variable de type nom. Si une variable de type a n'importe quelle utilisation (par exemple, dans la méthode de la mise en œuvre ou comme une contrainte pour les autres type), alors il est logique de les nommer, sinon vous pouvez utiliser?
, anonyme variable. Alors, ressemble juste un raccourci.En outre, la
?
syntaxe n'est pas évitable lorsque vous déclarez un champ:Je vais essayer de répondre à votre question, un par un.
Pas. La raison en est que la délimitée générique n'a pas défini le type de paramètre. Il est un inconnu. Tous il "sait", c'est que le "confinement" est d'un type
E
(quelle que soit défini). Donc, il ne peut pas vérifier et de justifier si la valeur fournie correspond à la délimitée type.Donc, il n'est pas judicieux d'avoir polymorphes comportements sur des caractères génériques.
La première option est préférable dans ce cas que
T
est toujours borné, etsource
va certainement avoir des valeurs (des inconnus) que les sous-classesT
.Donc, supposons que vous souhaitez copier tous les liste de nombres, la première option sera
src
, essentiellement, peut accepterList<Double>
,List<Float>
, etc. comme il existe une limite supérieure à la paramétrée type trouvé dansdest
.La 2ème option va vous forcer à se lier
S
pour chaque type que vous souhaitez copier, commeComme
S
est paramétrée type qui a besoin de liaison.J'espère que cette aide.
<S extends T>
états quiS
est un type paramétré, qui est une sous-classe deT
, donc il faut un type paramétré (pas de joker) qui est une sous-classe deT
.Une autre différence qui n'est pas répertorié ici.
Mais les suivantes entraînera une erreur de compilation.
Comme je le comprends, il y a un seul cas d'utilisation lorsque le générique est strictement nécessaire (c'est à dire peut exprimer quelque chose que vous ne pouvez pas l'exprimer avec des paramètres de type). C'est quand vous avez besoin de spécifier une limite inférieure.
En dehors de cela cependant des caractères génériques servent à écrire plus de code concis, comme indiqué par les instructions suivantes dans le document que vous mentionnez:
Principalement -> les caractères génériques appliquer des génériques sur le paramètre/l'argument de niveau d'une Non-méthode Générique.
Remarque. Il peut également être réalisée en genericMethod par défaut, mais ici, au lieu de ? nous pouvons utiliser les T lui-même.
paquet génériques;
AFIN générique a ses propres usecases comme ça.