Différence entre le type générique et le type de Joker
je suis un internaute novice dans Generic et ma question Est: quelle différence entre deux fonctions:
fonction 1:
public static <E> void funct1 (List<E> list1) {
}
fonction 2:
public static void funct2(List<?> list) {
}
Merci.
7 réponses
la première signature dit: list1 est une liste de Es.
Le deuxième dit: liste est une Liste d'instances d'un certain type, mais nous ne connaissons pas le type.
la différence devient évidente lorsque nous essayons de changer la méthode de sorte qu'il prend un deuxième argument, qui devrait être ajouté à la liste à l'intérieur de la méthode:
import java.util.List;
public class Experiment {
public static <E> void funct1(final List<E> list1, final E something) {
list1.add(something);
}
public static void funct2(final List<?> list, final Object something) {
list.add(something); // does not compile
}
}
le premier fonctionne bien. Et vous ne pouvez pas changer le deuxième argument en quoi que ce soit en fait la compilation.
en fait je viens de trouver une encore plus belle démonstration de la différence:
public class Experiment {
public static <E> void funct1(final List<E> list) {
list.add(list.get(0));
}
public static void funct2(final List<?> list) {
list.add(list.get(0)); // !!!!!!!!!!!!!! won't compile !!!!!!!!!
}
}
on pourrait se demander pourquoi nous avons besoin de <?>
alors qu'il ne restreint que ce que nous pouvons en faire (comme @Babu_Reddy_H l'a fait dans les commentaires). Je vois les avantages suivants de la version Joker:
-
L'appelant a à savoir moins sur l'objet qu'il passe. Par exemple, si j'ai une carte des listes:
Map<String, List<?>>
je peux passer ses valeurs à votre fonction sans spécifier le type des éléments de la liste. So -
si je distribue des objets paramétrés comme celui-ci, je limite activement ce que les gens savent de ces objets et ce qu'ils peuvent en faire (à condition qu'ils restent à l'écart du moulage dangereux).
ces deux-là ont un sens quand je les combine: List<? extends T>
. Par exemple, envisager une méthode List<T> merge(List<? extends T>, List<? extends T>)
, qui fusionne les deux listes à une nouvelle liste de résultats. Bien sûr, vous pourriez présenter deux autres paramètres de type, mais pourquoi voudriez-vous? Ce serait trop spécifier des choses.
- enfin les caractères génériques peuvent avoir des limites inférieures, donc avec les listes vous pouvez faire fonctionner la méthode
add
, alors queget
ne vous donne rien d'utile. Bien sûr, cela déclenche la question suivante: pourquoi les génériques n'ont-ils pas des limites inférieures?
pour un plus en profondeur réponse voir: quand utiliser des méthodes génériques et quand utiliser wild-card? et http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ203
Génériques rend la collection plus sécurisé.
List<E>
: voici le paramètre Type, qui peut être utilisé pour déterminer le type de contenu de la liste, mais il y avait No
façon de vérifier ce qui était le contenu pendant le runtime
.
Generics are checked only during compilation time.
<? extends String>
: il a été spécialement construit en java, pour traiter le problème qui était avec le paramètre de Type. "? extends String"
signifie que cette liste peut avoir
objects which IS-A String.
pour eg:
Animal de la classe La classe de chien s'étend Animal Tiger classe étend Animal
ainsi en utilisant "public void go(ArrayList<Animal> a)"
will NOT accept
chien ou tigre comme son contenu mais Animal.
"public void go(ArrayList<? extends Animal> a)"
est ce qui est nécessaire pour faire le ArrayList take in Dog and Tiger type.
vérifier les références dans Head First Java.
La première est une fonction qui accepte un paramètre qui doit être une liste d'éléments de E type.
le deuxième type d'exemple n'est pas défini
List<?> list
ainsi vous pouvez passer la liste de n'importe quel type d'objets.
Liste comme un type de paramètre dit que le paramètre doit être une liste d'éléments avec n'importe quel type d'objet. En outre, vous pouvez lier le paramètre E
pour déclarer des références à la liste des éléments à l'intérieur du corps de la fonction.
la liste en tant que type de paramètre a la même sémantique, sauf qu'il n'y a pas d'autre moyen de déclarer les références aux éléments de la liste que d'utiliser Object
. D'autres messages donnent des différences subtiles supplémentaires.
j'explique habituellement la différence entre < E > et < ? > par comparaison avec les quantifications logiques, c'est-à-dire la quantification universelle et existentielle.
- correspond à " forall E, ..."
- correspond à "il existe quelque chose(indiqué par ) tel que ...."
par conséquent, la déclaration de méthode générique suivante signifie que, pour tout type de classe E , nous définissons funct1
public static <E> void funct1 (List<E>; list1) {
}
la déclaration de méthode générique suivante signifie que, pour une classe existante indiquée par < ? >, nous définissons funct2
.
public static void funct2(List<?> list) {
}
(depuis votre édition) ces deux signatures de fonction ont le même effet à l'extérieur du code -- ils prennent tous les deux n'importe quel List
comme argument. Un générique est équivalent à un paramètre de type qui est utilisé qu'une seule fois.
en plus de ces différences mentionnées ci-dessus, il y a aussi une différence supplémentaire: vous pouvez explicitement définir les arguments de type pour l'appel de la méthode générique:
List<Apple> apples = ...
ClassName.<Banana>funct2(apples); // for some reason the compiler seems to be ok
// with type parameters, even though the method has none
ClassName.<Banana>funct1(apples); // compiler error: incompatible types: List<Apple>
// cannot be converted to List<Banana>
( ClassName
est le nom de la classe contenant les méthodes.)