Очевидно, что поведение javac6 разумное, а javac7 - нет.
К сожалению, согласно букве спецификации, javac7 прав.
Это связано с корнем всего зла в стирании java-типа. Мотивация состоит в том, чтобы генерировать API-интерфейсы сбора, не нарушая старый код, который ссылается на старый, не-обобщенный API-интерфейс сбора. Для краткости обозначим это самая глупая мотивация .
При компиляции BPrime.<Object>make()
сначала javac должен выяснить класс, содержащий метод. Это легко класс B'
. (http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.1)
Тогда нам нужно знать все методы класса B'
, включая унаследованные. Это зависит от того, скрывает ли метод make()
( mb ) в B'
метод make()
( ma ) в A
; что сводится к тому, является ли подпись mb подписью ma . (http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8)
Существование концепции подписи также должно служить глупейшей мотивации . В противном случае нам нужно беспокоиться только об одних и тех же сигнатурах при определении методов переопределения и сокрытия.
Но на этот раз это не проблема. По определению, mb не является подписью ma , поэтому ma наследуется в классе B'
. Таким образом, класс B'
имеет два make()
метода.
Следующим шагом является определение потенциально применимых методов. Правило гласит (http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.2.1)
Если вызов метода включает в себя явные параметры типа, а элемент является универсальным методом, тогда число фактических параметров типа равно числу параметров формального типа.
Это означает, что ma применимо к выражению BPrime.<Object>make()
, потому что ma является , а не универсальным методом. Что?!
Спецификация объясняет
Из вышеприведенного предложения следует, что неуниверсальный метод может быть потенциально применим к вызову, который предоставляет явные параметры типа. Действительно, это может оказаться применимым. В таком случае параметры типа будут просто игнорироваться.
Это правило вытекает из вопросов совместимости и принципов заменяемости. Поскольку интерфейсы или суперклассы могут генерироваться независимо от их подтипов, мы можем переопределить универсальный метод с неуниверсальным. Однако переопределяющий (неуниверсальный) метод должен быть применим к вызовам универсального метода, включая вызовы, которые явно передают параметры типа. В противном случае подтип не будет заменять его обобщенный супертип.
Так что это также должно служить глупейшей мотивации , и мы должны допустить бессмысленный синтаксис, такой как
System.<String,Integer>currentTimeMillis();
Тогда, оба mb и ma применимы, таким образом, неоднозначность.