Компиляторы Oracle JDK и Eclipse JDT не согласны! Который компилирует это неправильно? Необычные дженерики и предположения
У меня есть кусок кода, который неуловимо компилируется между Oracle JDK 7 и Eclipse JDT 7, но поскольку я не уверен, что компилятор делает ошибку (-ы), я думал, что я должен попросить мнения здесь, прежде чем отправлять какие-либо отчеты об ошибках.
Это самый простой тест, который я смог бы продемонстрировать, чтобы продемонстрировать несогласованность:
interface Foo<S extends Foo<S, T>, T> {
// should this compile?
public <X extends Foo<S, Y>, Y> Y method1();
// what about this?
public <X extends Foo<? extends S, Y>, Y> Y method2();
}
Oracle JDK дает ошибку в методе 1, но не метод2, тогда как Eclipse не имеет проблемы ни с одним из методов. Я даже не уверен, что любой метод должен компилироваться...
Если ни один из методов не должен компилироваться для начала, то следующий момент является спорным, но я чувствую, что оба компилятора делают ошибку, если добавить следующий код:
interface Bar extends Foo<Bar, Integer> {
}
class Bug {
void bug() {
Bar bar = null;
Double bubble;
// these fail as expected...
bar.<Foo<Bar, Double>, Double> method1();
bar.<Foo<? extends Bar, Double>, Double> method2();
// ...but these don't even though the inferred parametrisations should be
// the same as above
Double bobble = bar.method1();
Double babble = bar.method2();
}
}
Когда мы предоставляем явные параметризации для методов 1 и 2, я не могу найти ни одного, который приведет к действительному вызову, который вернет Double (т.е. я не могу найти правильную параметризацию для X, когда Y параметризуется с помощью Double). Это поведение, которое я ожидаю, Y здесь должен быть параметризуемым с помощью Integer, насколько я могу видеть.
Когда мы позволяем компилятору вывести параметризацию, хотя и Oracle JDK, и Eclipse JDT позволяют коллировать с Y как двойным. Если вы наводите курсор на вызовы в Eclipse, он даже показывает параметризацию точно так же, как и наше ручное, которое не срабатывает, поэтому почему другое поведение?
(Причина присвоения новым переменным bobble и болтовня в этой точке состоит в том, что текст наведения показывает разные параметры - по какой-либо причине заменяет Double с Object - если мы снова назначаем bubble. Он по-прежнему компилирует как вызовы, так и присваивания Двойной, хотя, поэтому я не знаю, почему это так.)
Итак, это, возможно, еще один довольно неопределенный вопрос от меня, но может ли кто-нибудь здесь пролить свет на это для меня?
EDIT:
Отчет об ошибке с Eclipse: https://bugs.eclipse.org/bugs/show_bug.cgi?id=398011
Ответы
Ответ 1
Кажется, что компилятор делает какое-то упрощение. Foo.method1
и Foo.method2
объявляются с двумя параметрами X
и Y
, один из которых может быть определен во время вывода, но X
вообще не используется.
Поэтому, когда вы вызываете Double bobble = bar.method1()
X
, следует рассчитывать как extends Foo<Bar, Double>
, но компилятор решает отказаться от этого параметра, потому что он не используется.
Когда вы явно указываете параметры метода, компилятор должен проверять их правильность и терпеть неудачу, как ожидалось.
Если вы измените любой из этих методов, чтобы принять аргументы типа X
, вы не получите эту неоднозначную ситуацию, потому что вы предоставите некоторую информацию для компилятора, которая будет использоваться для определения фактического X
.
В Eclipse было несколько таких ошибок, когда его компилятор вообще не обнаруживает ошибок, но javac
начинает жаловаться на неправильные вызовы методов. Лучший способ избежать таких ошибок - использовать явные параметры, в этом случае большинство компиляторов ведут себя почти одинаково. Поэтому, если у вас есть проблемы с явными параметрами, лучше переконфигурируйте свои классы.