Java generics don't work

In my opinion, this should work, but it is not. What for? Source:

package javaapplication1; import java.util.*; class A { public static <K,V> Map<K,V> map() { return new HashMap<K,V>(); } } class Person {} class Dog {} public class JavaApplication1 { static void f(Map<Person, List<? extends Dog>> peopleDogList) {} public static void main(String[] args) { f(A.<Person, List<Dog>>map()); } } 

Very simple code. Compiler error: the f method in the JavaApplication1 class cannot be used to provide types; required: Map<Person, List<? extends Dog> Map<Person, List<? extends Dog> found: Map<Person, List<Dog>> Reason: actual arguments Map<Person, List<Dog>> cannot be converted to Map<Person, List<? extends Dog> Map<Person, List<? extends Dog> by converting a method call.

Map<Person, List<? extends Dog> Map<Person, List<? extends Dog> more general, so should the compiler be able to convert?

Also this: Map<Person, List<? extends Dog>> peopleDogList = A.<Person, List<Dog>>map(); Map<Person, List<? extends Dog>> peopleDogList = A.<Person, List<Dog>>map(); does not work. ? extends Dog ? extends Dog means an object that inherits Dog or Dog , so should the word Dog be ok?

+6
source share
2 answers

Map<Person, List<Dog>> not compatible with Map<Person, List<? extends Dog>> Map<Person, List<? extends Dog>> . In this case, it is expected that the map value type will be List<? extends Dog> List<? extends Dog> , not something that can be converted to the same thing. But if you used the parameter Map<Person, ? extends List<? extends Dog>> Map<Person, ? extends List<? extends Dog>> Map<Person, ? extends List<? extends Dog>> for f , it will work.

Here is a simple example that includes more basic types:

 Map<String, List<?>> foo = new HashMap<String, List<Object>>(); // error Map<String, ? extends List<?>> foo = new HashMap<String, List<Object>>(); // ok 

OP asks why this is happening. The simple answer is that type parameters are invariant, not covariant. That is, the specified type Map<String, List<?>> , the type of the map value must be exactly List<?> , And not something similar to it. What for? Imagine if covariant types are allowed:

 Map<String, List<A>> foo = new HashMap<>(); Map<String, List<?>> bar = foo; // Disallowed, but let suppose it allowed List<B> baz = new ArrayList<>(); baz.add(new B()); bar.put("baz", baz); foo.get("baz").get(0); // Oops, this is actually a B, not an A 

Unfortunately, a violation of foo.get("baz").get(0) as A violated.

Now suppose we do it right:

 Map<String, List<A>> foo = new HashMap<>(); Map<String, ? extends List<?>> bar = foo; List<B> baz = new ArrayList<>(); baz.add(new B()); bar.put("baz", baz); // Disallowed 

There, the compiler tried to put an incompatible list in foo (via the alias bar ). That is why it is required ? extends ? extends

+13
source

The problem is that you are trying to pass a HashMap<Person, List<Dog>> method waiting for Map<Person, List<? extends Dog>> Map<Person, List<? extends Dog>> .

The reason the compiler won't let you do this is because the method you call can do what is allowed on Map<Person, List<? extends Dog>> Map<Person, List<? extends Dog>> , but not allowed on HashMap<Person, List<Dog>> . For example, he might try to add an entry to a map where the value is ArrayList<Rottweiler> . Is this normal for Map<Person, List<? extends Dog>> Map<Person, List<? extends Dog>> (if Rottweiler extends Dog ), but not for any type Map<Person, List<Dog>> .

0
source

Source: https://habr.com/ru/post/952165/


All Articles