Компиляторы 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 начинает жаловаться на неправильные вызовы методов. Лучший способ избежать таких ошибок - использовать явные параметры, в этом случае большинство компиляторов ведут себя почти одинаково. Поэтому, если у вас есть проблемы с явными параметрами, лучше переконфигурируйте свои классы.