I tried to write a utility method which would populate a map from a key and a value array, no matter what the data type of key and value would be.
public static <K,V> Map<K,V> fillMap(Map<K,V> map, K[] keys, V[] values) {
int l= keys.length;
for (int i=0; i<l; i )
map.put(keys[i], values[i]);
return map;
}
Then I called the method with
HashMap<Integer, String> map= new HashMap<>();
Integer[] keys= IntStream.range(0, 12).boxed().toArray(Integer[]::new);
String[] values= new String[] {"Jan","Feb","Mar","Apr","Mai","Jun",
"Jul","Aug","Sep","Okt","Nov","Dez"};
map= MyUtil.fillMap(map, keys, values);
and received the error:
incompatible types: no instance(s) of type variable(s) K,V exist so that Map<K,V> conforms to HashMap<Integer,String>
All attempts to replace <K,V> by variations of <? extends Object> and the like failed so far.
How can this be fixed?
CodePudding user response:
Problem
In the code presented, we declare:
HashMap<Integer, String> map = new HashMap<>();
and
public static <K,V> Map<K,V> fillMap(Map<K,V> map, K[] keys, V[] values)
Hence, if we call
map = MyUtil.fillMap(map, keys, values);
we try to assing a Map<...>
(returned by MyUtil::fillMap
) to a HashMap<...>
. This cannot work since a Map
is not a HashMap
.
Possible solutions
There are two ways that come to my mind to fix this issue:
- either change the type of
map
, - or make the return-type of
MyUtil::fillMap
generic.
1. change the type of map
:
We can change the type of map
from HashMap<...>
to Map<...>
:
Map<Integer, String> map = new HashMap<>();
...
map = MyUtil.fillMap(map, keys, values);
2. Make the return-type of MyUtil::fillMap
generic:
By adding an additional generic parameter, we can make the concrete implementation of the return type generic as well:
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>();
final Integer[] keys = IntStream.range(0, 12).boxed().toArray(Integer[]::new);
final String[] values = new String[] {"Jan", "Feb", "Mar", "Apr", "Mai", "Jun",
"Jul", "Aug", "Sep", "Okt", "Nov", "Dez"};
map = MyUtil.fillMap(map, keys, values);
System.out.println(map);
}
public static <K, V, M extends Map<K, V>> M fillMap(M map, K[] keys, V[] values) {
final int l = keys.length;
for (int i = 0; i < l; i ) {
map.put(keys[i], values[i]);
}
return map;
}
Bonus: state-free construction of the return-value
If it is not necessary to pass-in the concrete implementation of the map used to the method, I would propose a third option that creates the map to return within the method:
public static <K, V> Map<K, V> fillMap(K[] keys, V[] values) {
return IntStream.range(0, keys.length)
.boxed()
.collect(Collectors.toMap(
index -> keys[index],
index -> values[index]));
}