Почему этот код не компилируется, ссылаясь на вывод типа в качестве причины? - PullRequest
0 голосов
/ 14 февраля 2019

Вот минимальный пример кода, с которым я работаю:

public class Temp {
    enum SomeEnum {}

    private static final Map<SomeEnum, String> TEST = new EnumMap<>(
               Arrays.stream(SomeEnum.values())
                     .collect(Collectors.toMap(t -> t, a -> "")));

}

Вывод компилятора:

Temp.java:27: error: cannot infer type arguments for EnumMap<>
    private static final Map<SomeEnum, String> TEST = new EnumMap<>(Arrays.stream(SomeEnum.values())
                                                      ^

Я обнаружил, что это можно обойти, заменивt -> t с Function.identity() или (SomeEnum t) -> t, но я не понимаю, почему это так.Какие ограничения в javac вызывают такое поведение?

Первоначально я обнаружил эту проблему с java 8, но убедился, что она все еще возникает с компилятором java 11.

Ответы [ 2 ]

0 голосов
/ 14 февраля 2019

Мы можем упростить пример далее:

Объявление метода, подобного

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.

0 голосов
/ 14 февраля 2019

Если мы явно указываем тип вместо использования оператора diamond, тогда он успешно компилируется.Ниже приведен код для того же самого:

private static final Map<SomeEnum, String> TEST = new EnumMap<SomeEnum, String>(
            Arrays.stream(SomeEnum.values())
                    .collect(Collectors.toMap(t -> t, a -> "")));

Для справки, что согласно другой ссылке , в некоторых сценариях оператор Diamond не поддерживается.Можно было бы еще покопаться, если фрагмент кода, о котором идет речь, попал в эту корзину.

...