Неоднозначный вызов Varargs Java

Я немного смущен методами Java varargs :

public static int sum(int ...a) { return 0; } public static double sum(double ...a) { return 0.0; } 

Когда я пытался вызывать sum() без передачи каких-либо аргументов, тогда была вызвана int версия метода. Я не понимаю, почему; обычно компилятор должен вызывать ошибку.

Напротив, следующий fragment кода генерирует ошибку компилятора, когда я пытаюсь вызывать sum без каких-либо аргументов:

 public static int sum(int ...a) { return 0; } public static boolean sum(boolean ...a) { return true; } 

Общее правило, которое применяется здесь, следующее: если одна сигнатура метода строго более конкретна, чем другая, тогда Java выбирает ее без ошибок.

Интуитивно, сигнатура метода более конкретна, если вы можете полностью удалить ее, а другая, менее конкретная, применима к каждому существующему вызову.

Когда представлен выбор между sum(int... args) сигнатур sum(int... args) и sum(double... args) , sum(int... args) подписи sum(int... args) более конкретна, поскольку любое обращение этого метода также может быть передано sum(double... args) путем применения расширяющегося преобразования. То же самое не выполняется для метода sum(boolean... args) , который нельзя преобразовать аналогичным образом.

Спецификация языка Java, версия SE 8:

15,12. Выражения вызова метода

15.12.2.5. Выбор наиболее конкретного метода

Язык программирования Java использует правило, в котором выбран наиболее специфический метод.

Один применимый метод m1 более конкретный, чем другой применимый метод m2, для вызова с выражениями аргументов e1, …, ek, если выполняется одно из следующих утверждений:

  • m2 не является общим, а m1 и m2 применимы строгим или свободным вызовом, а где m1 имеет формальные типы параметров S1, …, Sn и m2 имеет формальные типы параметров T1, …, Tn, тип Si больше специфический, чем Ti для аргумента ei для всех i (1 ≤ i ≤ n, n = k).

Тип S более специфичен, чем тип T для любого выражения, если S <: T (§ 4.10).


4,10. Подтипы

4.10.1. Подтипирование среди примитивных типов

double> 1 float

float> 1 long

long> 1 int

Как уже упоминалось в этом ответе , при выборе метода перегрузки используется ряд правил.

Цитировать:

  1. Примитивное расширение использует наименьший аргумент метода
  2. Тип обертки не может быть расширен до другого типа Wrapper
  3. Вы можете вставить из int в Integer и расширить объект Object, но не до Long
  4. Расширяющиеся биты Бокс, Бокс бьет Вар-Аргс.
  5. Вы можете Вставить, а затем Widen (Int может стать объектом через Integer)
  6. Вы не можете Widen, а затем Box (An int не может стать Long)
  7. Вы не можете комбинировать var-args с расширением и боксом.

(Давайте переопределим правило 1 следующим образом: «Примитивное расширение использует максимально возможный аргумент метода».)

Поэтому, имея в виду эти правила, мы можем понять, что здесь происходит:

Согласно правилу номер один, примитивное расширение использует максимально возможный аргумент метода. Поскольку int представляет нечетное число (например, 1 ), а double – десятичным числом с точностью, превышающей 32 байта, чем значение для float (например, 1.0 ), мы можем сказать, что int s «меньше, чем “или” меньше, чем ” double s, и по этой логике int s может быть« повышен »до double s, а double s может быть« понижен »до int s.

Проще говоря, примитив, который может быть расширен до другого примитива (например, int -> float -> double ), более специфичен, чем другой. Например, int более специфичен, чем double потому что 1 можно повысить до 1.0 .

Когда вы не передали никаких аргументов этим перегруженным методам vararg с тем же именем, так как возврат фактически тот же (0 и 0.0 соответственно), компилятор предпочел бы использовать метод, который принимает vararg типа int поскольку он больше специфический .

Итак, когда вы ввели те же методы, которые принимают в int и boolean s (типы, которые не могут быть расширены друг к другу) соответственно, компилятор теперь не может выбрать метод для использования, поскольку int s не может быть «продвинут» или «понижен в должности» «как int s, float s и double s. Следовательно, он выдает ошибку компиляции.

Надеюсь, это поможет вам понять, что происходит.

  • Преимущество методов set и get vs public variable
  • Вызов метода с приемником указателя объектом вместо указателя на него?
  • Безопасный метод Python для получения значения вложенного словаря
  • Вызов метода перехвата в Objective-C
  • В чем разница между методом и функцией?
  • Перейти к встроенному методу дочернего метода struct call вместо родительского метода
  • Если оператор using == дает неожиданный результат
  • Как перехватить вызов метода со стандартными функциями Java (без AspectJ и т. Д.)?
  • Что делает метод registerNatives ()?
  • Синхронизированные статические методы Java: блокировка объекта или classа
  • Как передать несколько параметров в Objective-C?
  • Interesting Posts

    Dropbox выборочная синхронизация – возможно ли, чтобы новые папки, созданные на других устройствах, не были автоматически добавлены в папки синхронизации на текущем устройстве?

    Алгоритм поиска двух повторяющихся чисел в массиве без сортировки

    Linux Hibernate после ожидания

    Как программно доказать, что StringBuilder не является streamобезопасным?

    Как добавить параметры в HttpURLConnection с помощью POST с помощью NameValuePair

    Разница между параметрами и индексами типа?

    Как включить поддержку jQuery в Aptana Studio 3

    Как я могу автовоспроизвести видео с помощью нового стиля встраиваемого кода для Youtube?

    gson.toJson () выбрасывает StackOverflowError

    Почему «переполнение: авто» очищает всплывающие windows? И почему нужны чистые поплавки?

    std :: enable_if: параметр vs template

    Лучший способ сохранить данные на iPhone

    правильная аннотация для спящего режима для байта

    Контроль доступа на основе ролей (RBAC) и контроль доступа на основе требований (CBAC) в ASP.NET MVC

    Где ConfigurationGenerateSchemaCreationScript () переместился в Hibernate 5

    Давайте будем гением компьютера.