Ответ 1
Чтобы быть более общим
void accept(Map<String, ? extends List<?>> multiMap)
Почему пытается скомпилировать
public class GenericsFail {
public static void main(String[] args) {
accept(new HashMap<String, List<String>>());
}
public static void accept(Map<String, List<?>> multiMap) {}
}
введите ошибку
GenericsFail.java:7: error: method accept in class GenericsFail cannot be applied to given types;
accept(new HashMap<String, List<String>>());
^
required: Map<String,List<?>>
found: HashMap<String,List<String>>
reason: actual argument HashMap<String,List<String>> cannot be converted to Map<String,List<?>> by method invocation conversion
Подстановочный знак разрешен только в том случае, если он не вложен внутри List
.
Чтобы быть более общим
void accept(Map<String, ? extends List<?>> multiMap)
Причина в том, что ?
in List<?>
может быть "чем угодно", но другое "что угодно" в каждой записи Map
. То есть он принимал бы List<String>
в одной записи и List<Integer>
в другой.
Но вы проходите в Map
, который имеет один и тот же тип List
в каждой записи, поэтому тип не связан таким же образом или с той же степенью свободы.
"Фиксировать" - это блокировка типа для определенного типа, но все еще "что-то" - одно и то же "что угодно" в каждой записи, набрав метод:
public static <T> void accept(Map<String, List<T>> multiMap) // complies
или если вашему методу действительно не нужно знать какой тип, используйте шаблон для обертывания типа:
public static void accept(Map<String, ? extends List<?>> multiMap) // compiles
Эта последняя версия работает, потому что тип списка, хотя и является подстановочным знаком, привязан к неизвестному, но последовательному типу при вызове.
Я нахожу, что типизированная версия легче читать (и код), а тип используется для использования, если вы позже решите, что ваш метод должен знать тип.
Вот краткий пример того, почему компилятор не может принять этот параметр:
public static void main(String[] args) {
//if this would be accepted here ...
accept(new HashMap<String, List<String>>());
}
public static void accept(Map<String, List<?>> multiMap) {
//this is valid considering the type of multiMap parameter,
//but would not work with the actual passed parameter.
multiMap.put("test", new ArrayList<Integer>());
}