Подтвердить что ты не робот

Смешивание параметров вложенного типа и подстановочных знаков в Java

Почему пытается скомпилировать

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.

4b9b3361

Ответ 1

Чтобы быть более общим

void accept(Map<String, ? extends List<?>> multiMap)

Ответ 2

Причина в том, что ? 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

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


Я нахожу, что типизированная версия легче читать (и код), а тип используется для использования, если вы позже решите, что ваш метод должен знать тип.

Ответ 3

Вот краткий пример того, почему компилятор не может принять этот параметр:

    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>());
    }