Why does this code fail to compile, citing type inference as the cause?
We can simplify the example further:
Declaring a method like
static <K,V> Map<K,V> test(Map<K,? extends V> m) {
return Collections.unmodifiableMap(m);
}
the statement
Map<SomeEnum, String> m = test(Collections.emptyMap());
can be compiled without problems. Now, when we change the method declaration to
static <K extends Enum<K>,V> Map<K,V> test(Map<K,? extends V> m) {
return Collections.unmodifiableMap(m);
}
we get a compiler error. This indicates that the difference between wrapping your stream expression with new EnumMap<>(…)
and new HashMap<>(…)
lies in the type parameter declaration of the key type, as EnumMap
’s key type parameter has been declared as K extends Enum<K>
.
It seems to be connected with the self-referential nature of the declaration, e.g. K extends Serializable
does not cause an error while K extends Comparable<K>
does.
While this fails in all javac
versions from Java 8 to Java 11, the behavior is not as consistent as it seems. When we change the declaration to
static <K extends Enum<K>,V> Map<K,V> test(Map<? extends K,? extends V> m) {
return Collections.unmodifiableMap(m);
}
the code can be compiled again under Java 8, but still fails with Java 9 to 11.
To me, it’s illogical that the compiler infers SomeEnum
for K
(which would match the bound Enum<K>
) and String
for V
, but fails to infer these types when a bound has been specified for K
. So I consider this a bug. I can’t preclude that there’s a statement somewhere in the depth of the specification which allows to conclude that a compiler should behave that way, but if so, the specification should be fixed as well.
As said by others in the comments section, this code can be compiled with Eclipse without problems.