Подтвердить что ты не робот

Почему этот метод перегружает неоднозначно?

public class Primitive {
    void m(Number b, Number ... a) {} // widening, autoboxing->widening->varargs

    void m(byte b, Number ... a) {} // unboxing, autoboxing->widening->varargs

    public static void main(String[] args) {
        Byte b = 12;
        Primitive obj = new Primitive();
        obj.m(b, 23);
    }
}

Я уже обыскал и обнаружил, что расширение приоритета выше, чем unboxing, поэтому в вышеприведенном вызове метода должен быть вызван первый метод, потому что второй параметр одинаковый для обоих. Но этого не происходит. Можете ли вы рассказать о PLS?

4b9b3361

Ответ 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. И, вероятно, есть целая команда инженеров, занимающихся перекрестной проверкой и проверкой этого раздела. Поэтому любое выражение или любые попытки рассуждения следует принимать с огромным количеством соли. (Даже если это исходит от вышеупомянутых инженеров). Но я попробую, по крайней мере, изложить соответствующие части, которые другие могут ссылаться для дальнейшего анализа:

Учитывая раздел о

и учитывая, что оба метода являются "потенциально применимыми методами" (JLS7/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, но эта фаза никогда не достигается.