Ссылки методов на необработанные типы вредны?

Код ниже содержит ссылку на Enum::name (обратите внимание на отсутствие параметра типа).

public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
    return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), Enum::name);
}

public static <T, R> ColumnType<T, R> simpleColumn(BiFunction<JsonObject, String, T> readFromJson,
        Function<T, R> writeToDb) {
 // ...
}

Javac сообщает предупреждение во время компиляции:

[ПРЕДУПРЕЖДЕНИЕ] обнаружен необработанный тип: в java.lang.Enum отсутствуют аргументы типа для универсального класса java.lang.Enum.

Изменение выражения на Enum<T>::name приводит к исчезновению предупреждения.

Однако Idea помечает версию Enum<T>::name предупреждением о том, что:

Аргументы явного типа могут быть выведены

В свою очередь Eclipse (ECJ) не сообщает о каких-либо проблемах ни с одной из формулировок.

Какой из трех подходов правильный?

С одной стороны, необработанные типы довольно неприятны. Если вы попытаетесь указать аргумент другого типа, например. Enum<Clause>::name приведет к сбою компиляции, так что это дополнительная защита.

С другой стороны, приведенная выше ссылка эквивалентна e -> e.name() lambda, и эта формулировка не требует аргументов типа.

Окружающая среда:

  • Ява 8u91
  • ИДЕЯ 15.0.3 Сообщество
  • ЕСП 4.5.2

person Jakub Bochenski    schedule 12.05.2016    source источник


Ответы (1)


Не существует такой вещи, как «необработанная ссылка на метод». Несмотря на то, что необработанные типы существуют для облегчения миграции кода, предшествующего Generics, не может быть никакого использования ссылок на методы до Generics, поэтому не существует «режима совместимости», и вывод типов является нормой. Спецификация языка Java §15.13. Справочные выражения методов указывают:

Если метод или конструктор является универсальным, соответствующие аргументы типа могут быть либо выведены, либо предоставлены явно. Точно так же аргументы универсального типа, упомянутые в выражении ссылки на метод, могут быть предоставлены явно или выведены.

Выражения ссылок на методы всегда являются поливыражениями.

Таким образом, хотя вы можете называть тип перед :: «необработанным типом», когда он ссылается на универсальный класс без указания аргументов типа, компилятор все равно выведет сигнатуру универсального типа в соответствии с типом целевой функции. Поэтому выдавать предупреждение об «использовании необработанных типов» здесь не имеет смысла.

Обратите внимание, что, например.

BiFunction<List<String>,Integer,String> f1 = List::get;
Function<Enum<Thread.State>,String> f2 = Enum::name;

может быть скомпилирован с javac без каких-либо предупреждений (в спецификации указаны похожие примеры, где тип должен быть выведен), тогда как

Function<Thread.State,String> f3 = Enum::name;

генерирует предупреждение. спецификация говорит об этом случае:

При втором поиске, если P1, ..., Pn не пусто и P1 является подтипом ReferenceType, то выражение ссылки на метод обрабатывается так, как если бы оно было выражением вызова метода с выражениями аргументов типы P2, ..., Pn. Если ReferenceType является необработанным типом и существует параметризация этого типа, G<...>, которая является супертипом P1, тип для поиска является результатом преобразования захвата (§5.1.10), примененного к G<...>;…

Таким образом, в приведенном выше примере компилятор должен вывести Enum<Thread.State> как параметризацию Enum, которая является супертипом Thread.State, чтобы найти подходящий метод и прийти к тому же результату, что и в примере f2. Каким-то образом это действительно работает, хотя и генерирует бессмысленное предупреждение необработанного типа.


Поскольку, по-видимому, javac генерирует это предупреждение только тогда, когда ему нужно искать подходящий супертип, для вашего случая есть простое решение. Просто используйте точный тип для поиска:

public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
    return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), T::name);
}

Это компилируется без каких-либо предупреждений.

person Holger    schedule 12.05.2016
comment
Спасибо за очень исчерпывающий ответ. Просто для ясности: нет возможности выполнять небезопасные операции с помощью необработанного ReferenceType? (Что-то вроде присвоения List<String> переменной List<Long> через приведение List) - person Jakub Bochenski; 13.05.2016
comment
Для этого потребуются необработанные типы внутри функциональной сигнатуры целевого типа, но тогда, конечно, должно быть сгенерировано соответствующее предупреждение здесь. Например, рассмотрим BiConsumer<List,Object> add=List::add;, который выражает непроверенную операцию, но за это отвечает объявление BiConsumer, а не ссылка на метод. - person Holger; 13.05.2016