Ответ 1
Он не компилируется в JDK 1.5, 1.6 и 1.7, но работает в JDK 1.8.
Обновление. Похоже, что тот факт, что он работал с первыми версиями JDK8, был на самом деле ошибкой: он работал в JDK 1.8.0_05, но согласно этот вопрос и ответ medvedev1088, этот код будет не дольше компилироваться в 1.8.0_25, что является поведением, которое соответствует JLS
Я не думаю, что это ошибка, которая была исправлена. Вместо этого это скорее эффект изменений, связанных с механизмами вызова метода для лямбда-выражений в Java 8.
Большинство людей, вероятно, согласятся с тем, что раздел "Выражения вызова метода" на сегодняшний день является самой сложной непонятной частью спецификации Java Language Specification. И, вероятно, есть целая команда инженеров, занимающихся перекрестной проверкой и проверкой этого раздела. Поэтому любое выражение или любые попытки рассуждения следует принимать с огромным количеством соли. (Даже если это исходит от вышеупомянутых инженеров). Но я попробую, по крайней мере, изложить соответствующие части, которые другие могут ссылаться для дальнейшего анализа:
Учитывая раздел о
- Выражения вызова метода в JLS 7
- Выражения вызова метода в JLS 8
и учитывая, что оба метода являются "потенциально применимыми методами" (JLS7/JLS8), то соответствующий подраздел относится к
- Этап 3: Определить применимые методы переменной Arity в JLS7
- Фаза 3: Определить методы, применимые при вызове Variable Arity Invocation в JLS8
Для JLS 7 он устанавливает
Метод m является применимым методом переменной-степени тогда и только тогда, когда выполняются все следующие условия:
- Для 1 = я < n, тип ei, Ai, может быть преобразован путем преобразования вызова метода в Si.
- ...
(Другие условия относятся к формам обращения, которые здесь неактуальны, например, вызовы, которые действительно используют varargs или вызовы, которые включают в себя generics)
Обратившись к примеру: Метод применим для выражения фактического аргумента b
типа Byte
, когда b
можно преобразовать в соответствующий параметр формального метода с помощью преобразования вызова метода. Согласно соответствующему разделу о "Invocation Conversion" в JLS7 допускаются следующие преобразования:
- преобразование идентичности (§5.1.1)
- расширение примитивного преобразования (§5.1.2)
- расширение ссылочного преобразования (§5.1.5)
- преобразование бокса (§5.1.7), за которым следует расширенное ссылочное преобразование
- преобразование для распаковки (п. 5.1.8), за которым следует расширенное преобразование примитива.
Очевидно, что в соответствии с этой спецификацией существуют два метода:
-
m(Number b, Number ... a)
применим при расширении ссылочного преобразования -
m(byte b, Number ... a)
применим с помощью преобразования unboxing
Вы упомянули, что вы "... обнаружили, что расширение приоритета выше, чем распаковка", но здесь это не применимо: перечисленные выше условия не имеют никакого "приоритета". Они перечислены как разные варианты. Даже если первый метод был void m(Byte b, Number ... a)
, было бы применимо "преобразование идентичности", но оно все равно будет считаться только одним возможным преобразованием и вызвать метод ошибки из-за неоднозначности.
Итак, насколько я понял, это объясняет, почему с JDK7 работала не. Я не разобрался в деталях, почему он сделал работу с JDK8. Но определение применимости методов переменной arity слабо изменилось в Определить методы, применимые с помощью вызова переменной Arity в JLS 8:
Если m не является общим методом, то m применим при вызове переменной arity, если для 1 ≤ я ≤ k либо ei совместим в свободном контексте вызова с Ti, либо ei не имеет отношения к применимости (§ 15.12. 2.2).
(я еще не углублялся в определения "контекстов свободной связи" и раздела § 15.12.2.2, но это, по-видимому, является решающим различием здесь)
В стороне, еще раз ссылаясь на ваше утверждение, что вы "... обнаружили, что расширение приоритета выше, чем unboxing": это верно для методов, которые не включают в себя varargs (а это не так требуют преобразования вызова метода вообще). Если вы оставите varags в своем примере, то процесс поиска метода сопоставления начнется в Этап 1: Определите методы сопоставления Arity, применимые подтипом. Метод m(Number b)
уже применим для параметра Byte b
, поскольку Byte
является подтипом Number
. Не было бы причин переходить в Этап 2: Определить методы соответствия Arity, применимые с помощью преобразования Invoice Conversion. На этом этапе будет применено преобразование вызова метода через unboxing от Byte
до Byte
, но эта фаза никогда не достигается.