#java #generics
public staticboolean addToGroupMap(K key, V value, Map > checkMap){ assert checkMap!=null; boolean result = false; Collection vList = checkMap.get(key); if (vList==null){ checkMap.put(key, new ArrayList (Collections.singleton(value))); }else { vList.add(value); result = true; } return result; } Думал, что более менее разобрался с дженериками, однако, в упор не понимаю, почему в строке: checkMap.put(key, new ArrayList (Collections.singleton(value))); Компилятор выдает ошибку: Wrong 2nd argument type. Found: 'java.util.ArrayList', required: '? extends java.util.Collection Сначала в сигнатуре метода было : K key, V value, Map > checkMap. Однако теперь надо, чтобы в значении мапы была именно коллекция. При этом, нет желания менять в куче кода List<> в checkMap'e на Collection<>, да и почему это надо делать, если лист имплементирует коллекцию? UDP: Также, возникает вопрос. Почему, если заменить в сигнатуре ? extends Collection на ? super Collection, пропадает доступ к методам коллекции? Ведь вроде ? super Collection ограничивает самой коллекцией и ее родителями?
Ответы
Ответ 1
Забудем пока про конкретно ваш код и рассмотрим более простой пример. Допустим, есть вот такие классы: // Для демонстрации иерархии типов class A { } class B extends A { } class C extends B { } // Для демонстрации контейнера class S{ private V value; public V get() { return value; } public void set(V value) { this.value = value; } } Рассмотрим такой контейнер, как S. Каким будет возвращаемое значение для метода get? Этот метод вернет что-то, что является наследником B. То есть, что бы метод get ни вернул - это что-то можно записать в переменную типа B. S s; B v = s.get(); // Значение типа ? extends B всегда можно записать в переменную типа B Теперь рассмотрим метод set. Кажется, что все нормально? Но давайте сделаем вот так: S s = new S (); s.set(new B()); // Ошибка - значение типа B не может быть передано как параметр типа C Здесь я создал конкретный контейнер для примера. Даже если на самом деле используется S - компилятор должен обеспечить корректность кода в любом случае. Теперь рассмотрим такой контейнер как S. Метод set у него можно вызвать с параметром типа B: S s; s.set(new B()); // Значение типа B всегда можно передать как ? super B А вот метод get нормально вызвать не получится: S s = new S(); B v = s.get(); // Ошибка - попытались значение типа A записать в переменную типа B В итоге получается, что get-методы требуют отношения extends, а set-методы требуют отношения super. Если вашему коду нужно использовать оба типа методов - придется определять оба отношения одновременно, т.е. оставить просто . Возвращаюсь к вашему коду, можно заметить, что из параметра checkMap вы одновременно получаете данные (get) - и передаете их ему (put). Поэтому, вы не можете использовать ? extends ... в определении параметра. Единственный способ сделать оба вызова рабочими - использовать конкретный тип данных. Например, Map >. Если же вам нужно работать с разными отображениями - то сам тип коллекции надо делать обобщенным: Map где C extends Collection . Для того чтобы создать такую коллекцию, вам нужно будет принимать фабрику или класс как параметр: public static > boolean addToGroupMap(K key, V value, Map checkMap, Callable collectionFactory) { C vList = checkMap.get(key); checkMap.put(key, vList = collectionFactory.call()); } public static > boolean addToGroupMap(K key, V value, Map checkMap, Class collectionClass) { C vList = checkMap.get(key); checkMap.put(key, vList = collectionClass.newInstance()); } Смысл фабрики - в том, что ее создает тот код, который знает точный тип коллекции: Map > checkMap1 = new Map<>(); Callable > factory1 = () -> new ArrayList
(); Map > checkMap2 = new Map<>(); Callable > factory2 = () -> new HashSet (); Дальше фабрика по цепочке вызовов передается пока не придет в метод addToGroupMap.
Комментариев нет:
Отправить комментарий