Связанная с компилятором ошибка, связанная с шаблоном

Мне интересно, что не так с этим кодом:

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

Компилятор жалуется на сообщение об ошибке:

Тип несоответствия: невозможно преобразовать из Set<Map.Entry> Set<Map.Entry> Set<Map.Entry> для Set<Map.Entry> Set<Map.Entry>

Каким должен быть тип s ? Eclipse предлагает Set Но я пытаюсь получить более конкретную информацию.

Эта проблема решается в этом старом streamе Apache :

Проблема в том, что метод entrySet() возвращает Set> Set> Set> , что несовместимо с типом Set> Set> Set> . Легче описать, почему, если я отбрасываю extends K и extends V часть. Итак, у нас есть Set И Set> .

Первый, Set> – это набор Map.Entries разных типов – то есть это гетерогенная коллекция. Он может содержать Map.Entry и Map.Entry> и любую другую пару типов, все в одном наборе.

С другой стороны, Set> является однородной коллекцией той же (хотя и неизвестной) пары типов. Например, это может быть Set> , поэтому все записи в наборе ДОЛЖНЫ быть Map.Entry .

Суть проблемы заключается в том, что захват групповых символов верхнего уровня, что означает, что они по сути являются одноразовыми параметрами. Напротив, вложенные подстановочные знаки не захватывают и имеют несколько иное значение.

Итак, удаляя оценки для простоты, объявляя

 Map m; 

означает «карту определенного неизвестного типа ключей и некоторого определенного неизвестного типа значений».

Но объявление

 Set> s; 

означает «набор записей любого типа ключа и значения».

Так вот где вы столкнулись с проблемой, потому что выражение m.entrySet() не хочет возвращать это, а вместо этого «набор записей какого-то определенного неизвестного типа ключей и некоторых определенных неизвестных типов значений». И эти типы несовместимы, потому что generics не ковариантны : A Set не является Set .

(См. Этот увлекательный пост, который помогает разделить нюансы вложенных подстановочных знаков. Множественные подстановочные знаки для общих методов делают Java-компилятор (и меня!) Очень запутанным .)

Одним из способов является использование метода захвата-захвата , который использует тот факт, что параметры формального типа могут быть вложенными:

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

Это надуманный пример, поскольку String и Integer являются final , но он показывает концепцию.

Более простым способом является следующее:

 Set> s = m.entrySet(); 

Это означает, что добавление null элементов в s недопустимо, но в случае Set возвращаемого с помощью entrySet , методы add и addAll в любом случае не поддерживаются (благодаря newacct для уточнения этой точки ).

Давайте будем гением компьютера.