Un esercizio classico è quello di calcolare l'unione di due insiemi.
L'unione di due insiemi A e B è un nuovo insieme che contiene tutti gli elementi di A e tutti gli elementi di B.
Se volessi scrivere un metodo che calcola l'insieme unione a partire da due insiemi puoi farlo attraverso il metodo
public static <T> Set<T> union(Set<? extends T> a, Set<? extends T> b)
Infatti se supponi che T sia Number, il set a potrebbe essere un Set<Integer> e b un Set<Double>, o viceversa; o entrambi possono essere Set<Integer> o Set<Double> o Set<Long>; o addirittura uno dei due o entrambi possono essere Set<Number>.
Come posso utilizzare l'estensione <? extends ...?? potrei aggiungere alle lettere anche dei numeri ..
Tipo TreeSet<E extends .....qualcosa ??
String e Number hanno come antenato comune soltanto Object (a parte l'interfaccia Serializable) quindi l'unica soluzione è utilizzare un TreeSet<Object> o un Treeset<?> [*] ma in questo caso puoi inserire qualsiasi oggetto.
Inoltre nel codice che hai postato sarebbe comunque un errore utilizzare <? extends ...> per il TreeSet parola, poichè poco dopo stai utilizzando il metodo addAll() che, come ti è stato detto, quando si usa <? extends...> non è possibile andare a modificare la collezione.
Un altro esempio potrebbe essere un metodo che rimuove e restituisce il primo elemento da un buffer produttore.
public static <T> T get(List<? extends T> producer) {
if(producer.isEmpty()) return null;
T elem=producer.remove(0);
return elem;
}
Non a caso ho utlizzato il nome producer. Infatti esiste la seguente regola mnemonica:
PECS (
Producer
Extends
Consumer
Super).
Infatti quando si hanno dei produttori questi devono solo fornire, produrre appunto, elementi. Non devono immagazzinare, prendere in input nulla. E' lecito quindi usare <? extends...>. Invece se si usano dei consumatori, questi avranno la necessità di usare, consumare appunto, dei dati, magari immagazzinarli anche, in questa situazione si usa <? super...>.
Se invece hai necessità sia di esporre dei dati, sia di immagazzinarli, probabilmente i wildcard sono la scelta sbagliata.
[*]La differenza tra TreeSet<Object> e TreeSet<?> sta nel fatto che il primo è un tipo parametrizzato che contiene Object, permette di essere acceduto sia in lettura che in scrittura, il secondo invece può essere scritto anche come TreeSet<? extends Object> che può contenere oggetti che siano sottotipi di Object o Object stesso, ma può essere acceduta solo in lettura.