Quelle est la différence entre 'E', 'T', et '?'pour les génériques Java?


Je tombe sur du code Java comme ceci:

public interface Foo<E> {}

public interface Bar<T> {}

public interface Zar<?> {}

Quelle est la différence entre les trois ci-dessus et comment appellent-ils ce type de déclarations de classe ou d'interface en Java?

Author: Isaac Moore, 2011-05-15

6 answers

Eh bien, il n'y a pas de différence entre les deux premiers - ils utilisent simplement des noms différents pour le paramètre de type (E ou T).

Le tiers n'est pas une déclaration valide - ? est utilisé comme un générique, qui est utilisé lors de la fourniture d'un type argument, par exemple List<?> foo = ... signifie que foo fait référence à une liste d'un certain type, mais nous ne savons pas quoi.

Tout cela est génériques, ce qui est un très vaste sujet. Vous pouvez apprendre à ce sujet par la suite ressources, bien qu'il y en ait plus disponibles bien sûr:

 249
Author: Jon Skeet, 2017-05-30 17:03:52

C'est plus une convention qu'autre chose.

  • T est censé être un type
  • E est censé être un Élément de (List<E>: une liste d'Éléments)
  • K est la clé (dans un Map<K,V>)
  • V est une valeur (en tant que valeur de retour ou valeur mappée)

Ils sont entièrement interchangeables (nonobstant les conflits dans la même déclaration).

 227
Author: ratchet freak, 2015-11-09 14:29:39

Les réponses précédentes expliquent les paramètres de type (T, E, etc.), mais n'explique pas le caractère générique "?", ou les différences entre eux, donc je vais aborder cela.

Tout d'abord, pour être clair: les paramètres wildcard et type ne sont pas les mêmes. Lorsque les paramètres de type définissent une sorte de variable (par exemple, T) qui représente le type pour une portée, le caractère générique ne le fait pas: le caractère générique définit simplement un ensemble de types autorisés que vous pouvez utiliser pour un type générique. Sans aucune limite (extends ou super), le générique signifie "utiliser n'importe quel type ici".

Le générique viennent toujours entre crochets, et il n'a de sens que dans le contexte d'un type générique:

public void foo(List<?> listOfAnyType) {...}  // pass a List of any type

Jamais

public <?> ? bar(? someType) {...}  // error. Must use type params here

Ou

public class MyGeneric ? {      // error
    public ? getFoo() { ... }   // error
    ...
}

Il devient plus déroutant où ils se chevauchent. Par exemple:

List<T> fooList;  // A list which will be of type T, when T is chosen.
                  // Requires T was defined above in this scope
List<?> barList;  // A list of some type, decided elsewhere. You can do
                  // this anywhere, no T required.

Il y a beaucoup de chevauchement dans ce qui est possible avec les définitions de méthode. Les éléments suivants sont fonctionnellement identiques:

public <T> void foo(List<T> listOfT) {...}
public void bar(List<?> listOfSomething)  {...}

Donc, si il y a chevauchement, pourquoi utiliser l'un ou l'autre? Parfois, honnêtement, c'est juste du style: certaines personnes disent que si vous n'avez pas besoin de un paramètre de type, vous devriez utiliser un caractère générique juste pour rendre le code plus simple/plus lisible. Une différence principale que j'ai expliquée ci-dessus: les paramètres de type définissent une variable de type (par exemple, T) que vous pouvez utiliser ailleurs dans la portée; le caractère générique ne le fait pas. Sinon, il y a deux grandes différences entre les paramètres de type et le caractère générique:

Les paramètres de type peuvent avoir plusieurs classes de délimitation; le caractère générique ne peut pas:

public class Foo <T extends Comparable<T> & Cloneable> {...}

Le le caractère générique peut avoir des limites inférieures; les paramètres de type ne peuvent pas:

public void bar(List<? super Integer> list) {...}

Dans ce qui précède, le List<? super Integer> définit Integer comme une limite inférieure sur le caractère générique, ce qui signifie que le type de liste doit être Entier ou un super-type d'entier. La délimitation de type générique est au-delà de ce que je veux couvrir en détail. En bref, il vous permet de définir quels types un type générique peut être. Cela permet de traiter les génériques polymorphiquement. Par exemple avec:

public void foo(List<? extends Number> numbers) {...}

, Vous pouvez passer un List<Integer>, List<Float>, List<Byte>, etc. pour numbers. Sans délimitation de type, cela ne fonctionnera pas that c'est ainsi que sont les génériques.

Enfin, voici une définition de méthode qui utilise le caractère générique pour faire quelque chose que je ne pense pas que vous puissiez faire autrement:

public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
    numberSuper.add(elem);
}

numberSuper peut être une liste de Nombre ou n'importe quel supertype de Nombre (par exemple, List<Object>), et elem doit être un nombre ou n'importe quel sous-type. Avec toutes les limites, le compilateur peut être certain que le .add() est typesafe.

 141
Author: Hawkeye Parker, 2016-10-13 20:37:54

Une variable de type, , peut être n'importe quel type non primitif que vous spécifiez: n'importe quel type de classe, n'importe quel type d'interface, n'importe quel type de tableau, ou même une autre variable de type.

Les noms de paramètres de type les plus couramment utilisés sont:

  • E-Element (largement utilisé par le framework Java Collections)
  • Clé K
  • N-Nombre
  • Type T
  • Valeur V

En Java 7, il est permis d'instancier comme ceci:

Foo<String, Integer> foo = new Foo<>(); // Java 7
Foo<String, Integer> foo = new Foo<String, Integer>(); // Java 6
 29
Author: Uva, 2017-09-29 22:30:21

Les noms de paramètres de type les plus couramment utilisés sont:

E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types

Vous verrez ces noms utilisés dans l'API Java SE

 6
Author: Waqas Ahmed, 2019-05-17 16:03:09

Le compilateur fera une capture pour chaque caractère générique (par exemple, point d'interrogation dans la liste) quand il compose une fonction comme:

foo(List<?> list) {
    list.put(list.get()) // ERROR: capture and Object are not identical type.
}

Cependant un type générique comme V serait ok et en faire un méthode générique:

<V>void foo(List<V> list) {
    list.put(list.get())
}
 4
Author: Tiina, 2018-04-27 06:58:28