public static <K, V> boolean addToGroupMap(K key, V value, Map<K, ? extends Collection<V>> checkMap){ assert checkMap!=null; boolean result = false; Collection<V> vList = checkMap.get(key); if (vList==null){ checkMap.put(key, new ArrayList<V>(Collections.singleton(value))); }else { vList.add(value); result = true; } return result; } 

I thought that I figured out more or less with generics, however, I don’t understand why the line: checkMap.put(key, new ArrayList<V>(Collections.singleton(value))); The compiler gives an error:

Wrong 2nd argument type. Found: 'java.util.ArrayList', required: '? extends java.util.Collection

First, the method signature was: K key, V value, Map<K, List<V>> checkMap . But now it is necessary that the collection should be in the meaning of the map. At the same time, there is no desire to change the List<> heap in the checkMap'e to the Collection<> code, and why should it be done if the sheet implements the collection?

UDP: The question also arises. Why, if replaced in the signature ? extends Collection ? extends Collection on ? super Collection ? super Collection , access to methods of a collection vanishes? After all, like ? super Collection ? super Collection limit the collection itself and its parents?

  • 3
    Guess what? The extends Collection has the right to be, for example, Set, in which case an obvious logical error will occur. C just Collection<V> cannot be like this. - etki
  • And what difference does it make if Set that List implements the collection? - I. Perevoz
  • one
    @ I.Perevoz, ru.stackoverflow.com/a/546907/178988 - in your case, instead of a delegate, a collection, but the idea is the same - castes are not possible in this direction - a certain heir to the collection does not have to be an ArrayList. In theory, you need something like that , but I don’t know how to do it (and nothing good is being used). - Qwertiy
  • one
    <? extends Collection> <? extends Collection> means неизвестный тип (extending in this case, Collection). Since the type is not known, it is unknown whether it is a ArrayList superclass (or any other collection class) or not. And if it is not known, then your manipulations are unsafe. Запомните, дети: тарелька и вилька пишется без мягкого знака, а сол и фасол с мягким знаком. Запомните это дети, ибо понять этого не возможно! - Sergey
  • one
    @ I.Perevoz that you can formally apply this method to Set <V>, and do not work with the collection in its pure form. - etki

1 answer 1

For now, let's forget about your particular code and consider a simpler example. Suppose there are such classes:

 // Для демонстрации иерархии типов class A { } class B extends A { } class C extends B { } // Для демонстрации контейнера class S<V> { private V value; public V get() { return value; } public void set(V value) { this.value = value; } } 

Consider a container like S<? extends B> S<? extends B> . What will be the return value for the get method? This method returns something that is a descendant of B. That is, whatever the get method returns, it can be written to a variable of type B.

 S<? extends B> s; B v = s.get(); // Значение типа ? extends B всегда можно записать в переменную типа B 

Now consider the set method. It seems that everything is normal? But let's do this:

 S<? extends B> s = new S<C>(); s.set(new B()); // Ошибка - значение типа B не может быть передано как параметр типа C 

Here I created a specific container for an example. Even if S<B> is actually used - the compiler must ensure the correctness of the code in any case.

Now consider a container like S<? super B> S<? super B> . The set method can be called with the B type parameter:

 S<? super B> s; s.set(new B()); // Значение типа B всегда можно передать как ? super B 

But the get method normally does not work:

 S<? super B> s = new S<A>(); B v = s.get(); // Ошибка - попытались значение типа A записать в переменную типа B 

The result is that get methods require a extends relationship, and set methods require a super relationship. If your code needs to use both types of methods, you will have to define both relationships simultaneously, i.e. leave just <B> .


Returning to your code, you can see that from the checkMap parameter you simultaneously receive data (get) - and transfer it to it (put). Therefore, you can not use ? extends ... ? extends ... in the parameter definition. The only way to make both calls work is to use a specific data type. For example, Map<K, Collection<V>> .

If you need to work with different mappings, then the collection type itself should be generalized: Map<K, C> where C extends Collection<V> . In order to create such a collection, you will need to accept a factory or class as a parameter:

 public static <K, V, C extends Collection<V>> boolean addToGroupMap(K key, V value, Map<K, C> checkMap, Callable<? extends C> collectionFactory) { C vList = checkMap.get(key); checkMap.put(key, vList = collectionFactory.call()); } public static <K, V, C extends Collection<V>> boolean addToGroupMap(K key, V value, Map<K, C> checkMap, Class<? extends C> collectionClass) { C vList = checkMap.get(key); checkMap.put(key, vList = collectionClass.newInstance()); } 

The meaning of the factory is that it is created by the code that knows the exact type of the collection:

 Map<K, List<V>> checkMap1 = new Map<>(); Callable<List<V>> factory1 = () -> new ArrayList<V>(); Map<K, Set<V>> checkMap2 = new Map<>(); Callable<Set<V>> factory2 = () -> new HashSet<V>(); 

Then the factory on the call chain is passed until it comes to the addToGroupMap method.

  • Could you suggest the implementation of the factory in your case? I just do not understand how in this situation the factory will know which specific implementation to choose. - I. Perevoz
  • @ I.Perevoz and the factory should not choose it . She should know her. - Pavel Mayorov
  • @ I.Perevoz the one who knows the specific type of collection is the one who creates the factory. - Pavel Mayorov
  • Ie, as I understood, in .put I initialize the value in Entry, and then add elements to it (if necessary) by reference. - I. Perevoz 1:22
  • one
    @ I.Perevoz just added this in response. - Pavel Mayorov