Ответ 1
Очевидно, поведение 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, поэтому двусмысленность.