Несовместимые типы подстановочных знаков, которые должны быть совместимы

Исходя из этого вопроса, который предоставляет решение, но не объясняет его (к сожалению, ссылки в ответах сейчас мертвы):

Возьмите следующий метод:

void method(Map<?, ?> myMap) {
    Set<Map.Entry<?, ?>> set = myMap.entrySet();
    ...
}

Просто нет? Однако, это не в состоянии скомпилировать на jdk1.7.0_25:

incompatible types
required: java.util.Set<java.util.Map.Entry<?,?>>
found:    java.util.Set<java.util.Map.Entry<capture#1 of ?,capture#2 of ?>>

WTF? Map.entrySet() указывается как возвращающий объект типа Set<Map.Entry<K, V>>так что в приведенном выше примере, myMap.entrySet() возвращает Set<Map.Entry<?, ?>>, Но это не компилируется!

Еще более странно, из связанного вопроса наверху, изменение метода на это делает его компиляцией:

void method(Map<?, ?> myMap) {
    Set<? extends Map.Entry<?, ?>> set = myMap.entrySet();
    ...
}

WTF??? призвание entrySet на Map<?, ?> возвращает Set<Map.Entry<K, V>>, который нельзя присвоить переменной типа Set<Map.Entry<K, V>>, но это может к переменной типа Set<? extends Map.Entry<K, V>>?????

Кто-нибудь может пролить свет на то, что здесь происходит? И значит ли это, что всякий раз, когда я пишу метод, использующий подстановочный тип, по крайней мере, на 2 уровня, я должен помнить, чтобы сделать это ? extends ... где-то?

1 ответ

Решение

Каждый из тех? может варьироваться независимо, поэтому нет гарантии, что <?,?> в декларации myMap соответствует <?,?> в декларации set,

Что это означает, что когда-то у меня есть Set<Map<?,?>> Я могу поставить любой тип Map в этот набор, потому что Map<?,?> это супертип всех типов Map, Но это не свойство, которое Set<Map<String,Integer>> (например) имеет - это намного более ограничительно с точки зрения того, какие типы карт я могу поместить в него. Так Set<Map<?,?>> не супертип Set<Map<String,Integer>>, Но myMap.entrySet() может быть легко Set<Map<String,Integer>> в зависимости от того, что myMap является. Таким образом, компилятор должен запретить нам присваивать его переменной типа Set<Map<?,?>> и вот что происходит.

С другой стороны, Set<? extends Map<?,?>> это супертип Set<Map<String,Integer>>, так как Map<String,Integer> это подтип Map<?,?>, Так что можно назначить myMap.entrySet() к переменной типа Set<? extends Map<?,?>>,

Обратите внимание, что в этом нет ничего особенного String а также Integer здесь, но myMap должна быть карта чего-то!

Вы могли бы написать

<K, V> void method(Map<K, V> myMap) {
    Set<Map.Entry<K, V>> set = myMap.entrySet();
    ...
Другие вопросы по тегам