Nouveau tuto : https://docs.oracle.com/javase/tutorial/java/generics/index.html
Ancien tuto de 2005 : https://docs.oracle.com/javase/tutorial/extra/generics/index.html
Un type générique est une classe qui est paramétrable par des types (type parameters).
class Box<T> { ... }
Box<String> b = new Box<>();Une méthode générique introduit ses propres paramètres de types locaux.
public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
...
}
boolean same = Util.<Integer, String>compare(p1, p2);
boolean same = Util.compare(p1, p2); # inférence de typepublic <U extends Number> void inspect(U u)
public <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }
class D <T extends A & B & C> { /* ... */ } # les classes apparaissent obligatoirement avant les interfacesRésoudre les erreurs de compilation capture of avec les wildcards : https://dev.java/learn/generics/wildcards#wildcard-capture-and-helper-methods
Guide sur l'usage des wildcards lower et upper : https://dev.java/learn/generics/wildcards#guidelines-for-wildcard-use
Exemple : void process(List<? extends Foo> list)
Exemple : void printList(List<?> list)
Exemple : void addNumbers(List<? super Integer> list)
En Java, les generics ne sont implémentés et vérifiés qu'à la compilation.
A l'exécution, la notion de generics n'existe plus au détriment des raw types.
Retour et feedback de Brian Goetze sur le type erasure en 2020 : https://openjdk.org/projects/valhalla/design-notes/in-defense-of-erasure
https://dev.java/learn/generics/type-erasure/#bridge-methods
La réification est la possibilité pour un type d'avoir accès à toutes ses informations, ce qui n'est pas le cas des generics à cause du type erasure.
TLDR; En java, les generics (non unbounded) ne sont pas réifiés, à l'opposé des tableaux ou des types primitifs par exemple.
https://dev.java/learn/generics/type-erasure/#non-reifiable-types)
Variables mal typées provoquant un ClassCastException au runtime.
Exemple 1 (utilisation de rawtypes)
List<String> list = new ArrayList<String>();
List rawList = list;
rawList.add(8); // heap pollution
for (String str : list) { // ClassCastException at runtime
System.out.println(str);
}Exemple 2 (utilisation de generic varargs)
public static void faultyMethod(List<String>... l) {
Object[] objectArray = l; // Valid
objectArray[0] = Arrays.asList(42);
String s = l[0].get(0); // ClassCastException thrown here
}Dans le cas de l'utilisation de generic varargs, si l'on est assuré que la méthode ne produit pas de ClassCastException au runtime, on peut utiliser l'annotation @SafeVarargs.
@SafeVarargs
public static void safeVarargsMethod(List<String>... l) {
Object[] objectArray = l; // Valid
objectArray[0] = Arrays.asList("hello");
String s = l[0].get(0); // pas de ClassCastException ici
}Il est aussi possible d'utiliser @SuppressWarnings({"varargs"}), mais les avertissements depuis les points d'appel à la méthode ne seront pas supprimés.
https://docs.oracle.com/javase/tutorial/extra/generics/morefun.html
https://docs.oracle.com/javase/tutorial/extra/generics/convert.html
Voir l'exemple de la méthode public static <T extends Object & Comparable<? super T>> T Collections.max(Collection<? extends T> coll) !
Voir la page https://dev.java/learn/generics/restrictions pour les explications
- Les generics ne supportent pas les types primitifs : usage de l'autoboxing pour contourner
- Pas d'instanciation (directe) d'une variable d'un type paramétré : contournement possible via la reflexion
- Pas de champs
staticavec un type paramétré - Pas de instanceof sur les generics : les unbounded types sont autorisés cependant
- Pas de création de tableau de generics
- Pas d'héritage d'un généric à partir de la classe
Throwable - Pas de generic dans la clause
catch - Pas d'overloading de méthodes avec les generics (à cause du type erasure)
Par défaut, le compilateur affiche un avertissement quand un raw type est présent dans le code.
Pour savoir où, utiliser -Xlint:unchecked.
Pour désactiver complètement ce contrôle, utiliser -Xlint:-unchecked.
Pour désactiver une occurence dans le code, utiliser l'annotation @SuppressWarnings("unchecked").