Wildcards et PECS
? extends T donne un producteur que l'on peut seulement lire ; ? super T donne un consommateur dans lequel on peut seulement écrire. Producer Extends, Consumer Super.
Les génériques sont invariants : List<String> n’est pas un List<Object>.
Les wildcards réintroduisent la variance dont vous avez réellement besoin,
une direction à la fois.
void copy(List<? extends Number> src, List<? super Number> dst) {
for (Number n : src) { // lire depuis src est sûr
dst.add(n); // écrire dans dst est sûr
}
}
- Depuis
List<? extends Number>vous pouvez lire unNumber, mais vous ne pouvez rien ajouter — la liste réelle pourrait être unList<Integer>. - Vers
List<? super Number>vous pouvez ajouter n’importe quelNumber, mais une lecture ne renvoie qu’unObject.
Collections.copy et Stream.map sont tous deux écrits ainsi.
Le wildcard non borné
List<?> accepte n’importe quelle paramétrisation mais n’autorise aucun
ajout à part null. Ce n’est pas la même chose que le type brut List, qui
désactive complètement la vérification générique et produit des
avertissements non vérifiés.
Capture
void reverse(List<?> list) { doReverse(list); }
private <T> void doReverse(List<T> list) { /* T est maintenant nommable */ }
La méthode auxiliaire donne un nom au type capturé anonyme, ce qui est la
façon standard de contourner « impossible d’ajouter à un List<?> ».