Обобщения Java 8 Метод... не применим для аргументов в Eclipse
Во время миграции нашей кодовой базы с Java 1.7 на 1.8 мы получили сообщение об ошибке "Метод... не применим для аргументов" в нескольких местах кода, все по той же схеме при использовании обобщенных типов.
В настоящее время мы используем в основном Eclipse Mars
(4.5.2) на Windows 7
, но может подтвердить поведение с Neon
(4.6) тоже. Javac
так же как ecj
с уровнем соответствия 1.7 можно скомпилировать наш код без ошибок.
Вот минимальный, полный и проверяемый пример:
public class ComplexInterfaceTest {
public static class Foo {}
public interface Bar {
void print();
}
public static class SubFooBar extends Foo implements Bar {
public void print() {
System.out.println(this.getClass().getSimpleName());
}
}
public static class FooBar<T extends Foo & Bar> {
public static <T extends Foo & Bar> FooBar<T> makeFooBar() {
return new FooBar<>();
}
public void create(T value) {
value.print();
return;
}
}
public static class Base<T extends Foo> {}
public static class Subclass extends Base<SubFooBar> {
public void doSomething(SubFooBar value) {
// FooBar.<SubFooBar>makeFooBar().create(value);
FooBar.makeFooBar().create(value);
}
}
public static void main(String[] args) {
new Subclass().doSomething(new SubFooBar());
}
}
Теперь переключаем закомментированные строки в doSomething
Метод делает код для компиляции, поэтому у нас есть обходной путь. Тем не менее сообщение об ошибке кажется не правильным, как класс SubFooBar
простираться Foo
и реализует Bar
, так что выполняет контракт <T extends Foo & Bar>
, который требуется в <T extends Foo & Bar> FooBar<T> makeFooBar()
так на самом деле T
ИМО должен быть связан с SubFooBar
,
Я искал похожие вопросы и нашел их: Различия в выводе типов JDK8 javac/Eclipse Luna? Ошибка компиляции вывода типа в Eclipse с Java8, но не с Java7
Что заставляет меня думать, что это может быть ecj
ошибка. В этом курсе я также изучил Eclipse Bugzilla
но не смог найти ничего похожего, я видел это:
- 430686 подтверждено исправлено - у меня нет
- 440019 имеет отношение к скалярному типу - мой не
- 492838, 448793 имеют отношение к групповым символам - мой не
Сейчас Eclipse Bugzilla
обсуждения полны деталей о внутренней работе ecj
За которым я не всегда могу следить. Что я понимаю, хотя это общее согласие там, что Eclipse
компилятор должен строго следовать JLS
и не javac
(в случаях, когда это было неправильно), поэтому это не обязательно должно быть ошибкой в ecj
, Если бы это не было ecj
ошибка, то компиляция кода должна была javac
ошибка.
Что меня интересует - для тех, кто может анализировать процесс вывода типа моего фрагмента кода - должен ли код скомпилироваться или я допустил ошибку в кодировании?
РЕДАКТИРОВАТЬ
Как я и обещал опубликовать результаты моего доклада в Eclipse's Bugzilla
: дефект имеет идентификатор #497905 (Стефан Херрманн разместил ссылку в своем комментарии под принятым ответом) и в настоящее время предназначен для версии 4.7.
1 ответ
В методе
public void doSomething(SubFooBar value) {
FooBar.makeFooBar().create(value);
}
параметр типа T
метода makeFooBar()
никогда не будет выводиться как SubFooBar
, Тот факт, что вы собираетесь передать экземпляр SubFooBar
к create
метод впоследствии, не влияет на тип предыдущего выражения вызова FooBar.makeFooBar()
,
Это не меняется с типизацией цели Java 8, так как эта новая функция не работает с выражением-получателем при вызове цепочечного метода.
Таким образом, во всех версиях, тип выведен для T
из makeFooBar()
вызов будет типом пересечения Foo & Bar
поэтому тип результата FooBar<Foo&Bar>
, Это также то, что выводит Eclipse, даже если подсказка в редакторе может отображать что-то еще.
Это означает, что вы можете передать SubFooBar
экземпляр к create
метод как FooBar<Foo&Bar>.create(…)
ожидает случай Foo&Bar
а также SubFooBar
простирающийся Foo
и внедрение Bar
совместимо
Можно продемонстрировать, что Eclipse выводит тот же тип, что и все другие компиляторы, как вставка соответствующего приведения типа
public void doSomething(SubFooBar value) {
FooBar.makeFooBar().create((Foo&Bar)value);
}
делает ошибку компилятора исчезающей Так что проблема здесь не в выводе типа, а в том, что эта версия Eclipse считает, что SubFooBar
не присваивается Foo & Bar
,