Errore del compilatore relativo ai caratteri jolly limitati

Mi chiedo cosa c’è di sbagliato in questo codice:

Map  m = null; Set<Map.Entry> s = m.entrySet(); 

Il compilatore si lamenta con il messaggio di errore:

Tipo mancata corrispondenza: imansible convertire da Set<Map.Entry> Set<Map.Entry> Set<Map.Entry> per Set<Map.Entry> Set<Map.Entry>

Quale dovrebbe essere il tipo di s ? Eclipse suggerisce Set Ma sto cercando di ottenere qualcosa di più specifico.

Questo problema è affrontato in questo vecchio thread di Apache :

Il problema è che il metodo entrySet() restituisce un Set> Set> Set> , che è incompatibile con il tipo Set> Set> Set> . È più facile descrivere il motivo per cui se si rilascia l’ extends K e si extends V parte extends V Quindi abbiamo Set E Set> .

Il primo, Set> è un insieme di Map.Entries di tipi diversi – cioè è una collezione eterogenea. Potrebbe contenere un Map.Entry e un Map.Entry> e qualsiasi altro paio di tipi, tutti nello stesso set.

D’altra parte, Set> è una raccolta omogenea della stessa coppia (anche se sconosciuta) di tipi. Ad esempio potrebbe essere un Set> , quindi tutte le voci nel set DEVONO essere Map.Entry .

Il punto cruciale del problema è che i caratteri jolly di livello superiore vengono catturati , il che significa che sono essenzialmente parametri di tipo unico. Al contrario, i caratteri jolly nidificati non catturano e hanno un significato un po ‘diverso.

Quindi, rimuovendo i limiti per semplicità, dichiarando

 Map m; 

significa “una mappa di alcuni specifici tipi di chiavi sconosciute e alcuni specifici tipi di valori sconosciuti”.

Ma dichiarando

 Set> s; 

significa “un insieme di voci di qualsiasi tipo di chiave e valore”.

Ecco perché ci si mette nei guai perché l’espressione m.entrySet() non vuole restituirla ma piuttosto “un insieme di voci di alcuni specifici tipi sconosciuti di chiavi e qualche specifico tipo sconosciuto di valori”. E quei tipi sono incompatibili perché i generici non sono covarianti : un Set non è un Set .

(Vedi questo affascinante post, che aiuta a mettere a nudo le sfumature dei wildcard nidificati: più caratteri jolly con metodi generici rendono il compilatore Java (e io!) Molto confuso .)

Una soluzione alternativa consiste nell’utilizzare un metodo di acquisizione di cattura , che sfrutta il fatto che i parametri di tipo formale possono essere nidificati:

 private  void help(final Map map) { final Set> entries = map.entrySet(); // logic } ... Map m = null; help(m); 

Questo è un esempio forzato poiché String e Integer sono entrambi final , ma mostra il concetto.

Una soluzione più semplice è la seguente:

 Set> s = m.entrySet(); 

Ciò significa che l’aggiunta di elementi non null a s non è consentita, ma nel caso del Set restituito da entrySet , i metodi add e addAll non sono comunque supportati (grazie a newacct per chiarire questo punto ).