Here's a minimal example of the code I'm working with:
public class Temp {
enum SomeEnum {}
private static final Map<SomeEnum, String> TEST = new EnumMap<>(
Arrays.stream(SomeEnum.values())
.collect(Collectors.toMap(t -> t, a -> "")));
}
The compiler output is:
Temp.java:27: error: cannot infer type arguments for EnumMap<>
private static final Map<SomeEnum, String> TEST = new EnumMap<>(Arrays.stream(SomeEnum.values())
^
I have found that this can be worked around by replacing t -> t
with Function.identity()
or (SomeEnum t) -> t
, but I'm not understanding why this is the case. What limitation in javac is causing this behavior?
I originally found this issue with java 8, but have verified it still occurs with the java 11 compiler.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With