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.
Collection<V>cannot be like this. - etki<? 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