Мы можем упростить пример далее:
Объявление метода, подобного
static <K,V> Map<K,V> test(Map<K,? extends V> m) {
return Collections.unmodifiableMap(m);
}
, оператор
Map<SomeEnum, String> m = test(Collections.emptyMap());
может быть скомпилирован без проблем.Теперь, когда мы меняем объявление метода на
static <K extends Enum<K>,V> Map<K,V> test(Map<K,? extends V> m) {
return Collections.unmodifiableMap(m);
}
, мы получаем ошибку компилятора.Это означает, что разница между переносом выражения потока с new EnumMap<>(…)
и new HashMap<>(…)
заключается в объявлении параметра типа для типа ключа, поскольку параметр типа ключа EnumMap
был объявлен как K extends Enum<K>
.
Похоже, это связано с самообращающейся сущностью объявления, например, K extends Serializable
не вызывает ошибку, в то время как K extends Comparable<K>
.
Хотя это не удается во всех javac
версиях из Java 8в Java 11, поведение не так согласованно, как кажется.Когда мы изменяем объявление на
static <K extends Enum<K>,V> Map<K,V> test(Map<? extends K,? extends V> m) {
return Collections.unmodifiableMap(m);
}
, код может быть снова скомпилирован под Java 8, но все равно не работает с Java 9 до 11.
Для меня нелогично, что компилятор выводит SomeEnum
для K
(который будет соответствовать границе Enum<K>
) и String
для V
, но не выводит эти типы, когда граница была указана для K
. Так что я считаю это ошибкой .Я не могу исключить, что где-то в глубине спецификации есть утверждение, которое позволяет заключить, что компилятор должен вести себя таким образом, но если это так, то спецификация также должна быть исправлена.
Как говорили другиев разделе комментариев этот код можно без проблем скомпилировать с Eclipse.