In this answer I attempted to create a static utility method to make a List
into a Map
:
public static Map toMa
Your last line calls the method toMapBy
in which the compiler infers the type Student
for T
. So it obviously returns a List<Long, Student>
.
But generics aren't covariant!
That means, you cannot assign a List<Long, Student>
to a variable of type List<Long, Person>
, because they are not in a subtype relationship.
The solution is to use the subtype form:
Map<Long, ? extends Person> peopleById2 = toMapBy(students, Student::getId); // no compiler error
You could add a type parameter for the values of the map so they can be different from T:
public static <K, V, T extends V> Map<K, V> toMapBy(List<T> list,
Function<? super T, ? extends K> mapper) {
return list.stream().collect(Collectors.toMap(mapper, Function.identity()));
}
With this bit:
Map<Long, Person> peopleById1 = students.stream()
.collect(Collectors.toMap(Student::getId, Function.identity()));
Notice that you do not provide arguments to Function.identity()
. The compiler is free to infer it as Function.<Person>identity()
to resolve the difference imposed by the return value assignment.
This should be good enough for your purpose:
public static <K, T> Map<K, T> toMapBy(
List<? extends T> list, // <- note
Function<? super T, ? extends K> mapper
) {
...
}
Now the elements of the List can be a subtype of the Map values. Or you can define a third parameter like @Alex has suggested.