Premessa: nei generics il "wildcard" (il '?') NON indica che un oggetto di una classe generica può contenere qualunque cosa.
Un List<?> (il wildcard qui è unbounded, senza limite superiore o inferiore) NON vuol dire che la lista può contenere oggetti di qualunque tipo. Vuol dire che la parametrizzazione concreta non è nota a priori guardando solo questa tipizzazione. La parametrizzazione concreta è quella che si mette ad esempio quando si istanzia una implementazione concreta di una lista es. new ArrayList<Qualcosa>(). Qui è quel <Qualcosa> e generalmente è un tipo che esiste realmente es. String, Number, Integer, ecc...
E quando istanzi una parametrizzazione concreta non puoi usare ? (in nessuna delle forme con o senza bound).
Prendiamo il tuo esempio:
List<? super Number> lan;
poi
List<Object> lo = new ArrayList<Object>();
List<Number> ln = new ArrayList<Number>();
List<Integer> li = new ArrayList<Integer>();
quindi potrei pensare alle seguenti assegnazioni:
lan = lo; // OK
lan = ln; // OK
lan = li; // NOOOO!!!! Errore
Perché la terza non è corretta? Semplicemente perché <? super Number> impone un limite inferiore. Ovvero la parametrizzazione concreta che posso assegnare deve essere da Number in sù verso Object. Questo è quanto dice il linguaggio Java.
Qui non sto parlando del contenuto delle liste! Nel List<Object> lo posso tranquillamente inserire Integer, String, Date ecc... Qui si sta parlando della parametrizzazione concreta che ad un certo punto dovrai di certo usare, perché un oggetto concreto (es. una implementazione di lista) lo dovrai creare per fare qualcosa, no?
Ora vediamolo con un metodo:
public static void prova(List<? super Number> lan) { ........ }
Se io lo invocassi con quel lo:
prova(lo);
Tu potresti pensare che all'interno del metodo si possa fare:
lan.add(new Object());
perché hai passato un List<Object>. E invece no. Perché vedendo la lista come <? super Number> la parametrizzazione concreta ripeto che non la sai (e nemmeno il compilatore la sa).
L'unica cosa certa è quel limite inferiore. Qualunque cosa puoi lecitamente passare a 'prova', sicuramente è in grado di contenere un Double. Ma un Object no.
Se invece invocassi con prova(ln); (quindi passi un List<Number>) e il metodo potesse (idealmente) inserire un new Object() allora ..... tutta la teoria dei generics andrebbe a farsi friggere e non servirebbe a un bel nulla. Perché non sarebbe per niente type-safe.![]()