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>
perSet<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 unSet
> Set
> Set
, che è incompatibile con il tipo> Set
> Set
> Set
. È più facile descrivere il motivo per cui se si rilascia l’> extends K
e siextends V
parteextends V
Quindi abbiamoSet
ESet
.> Il primo,
Set
è un insieme di Map.Entries di tipi diversi – cioè è una collezione eterogenea. Potrebbe contenere un> Map.Entry
e unMap.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 extends String, ? extends Integer> 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 extends Map.Entry extends String, ? extends Integer>> 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 ).