Перегрузка Java - long и float

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

public static void main(String[] args) {
    long aLong = 123L;        
    foo(aLong);
}

private static void foo(double aDouble) {
    System.out.println("Foo aDouble");
}

private static void foo(Long aWrapperLong) {
    System.out.println("Foo Wrapper Long");
}

private static void foo(int anInt) {
    System.out.println("Foo Int");
}

private static void foo(float aFloat) {
    System.out.println("Foo Float");
}

Почему вызов разрешен для foo(float aFloat). Я понимаю следующее из JLS:

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

Я намеренно использовал Wrapper Long здесь, а не примитивный. Примитивный длинный 64-битный размер не заканчивается в foo(double aDouble), а 32-битный float foo(float aFloat).

Вопрос Почему Java неявно (без приведения) конвертирует` long` в `float`?, далее разъясняет ответ на этот вопрос.

Ответы

Ответ 1

Это из-за "наиболее специфического" правила в JLS # 15, который, в свою очередь, относится к JLS # 4.10, который, в свою очередь, ссылается на # 4.10.1, в котором говорится:

Следующие правила определяют отношение прямого супертипа среди примитивных типов:

  • double > 1 float

  • float > 1 long

  • long > 1 int

  • int > 1 char

  • int > 1 short

  • short > 1 byte

где "S > 1 T" означает, что "T является прямым подтипом S", согласно JLS # 4.10, непосредственно над этим разделом.

Итак, в этом случае, при отсутствии прямого совпадения на long и перед поиском автоматического бокса, компилятор выбирает ближайший доступный супертип, который равен float, по приведенным выше правилам.

Ответ 2

Цитата из JLS:

Процесс определения применимости начинается с определения потенциально применимые методы (§15.12.2.1).

Остальная часть процесса разделяется на три фазы, чтобы обеспечить совместимость с версиями языка программирования Java до Java SE 5.0. Фазы:

  • Первая фаза (§15.12.2.2) выполняет разрешение перегрузки без разрешая бокс или преобразование распаковки, или использование переменной arity вызов метода. Если на этом этапе не обнаружен какой-либо применимый метод то обработка продолжается до второй фазы.

Это гарантирует, что любые вызовы, которые были действительны в Java-программировании язык перед Java SE 5.0 не считается неоднозначным в качестве результата введения методов переменной arity, неявного бокса и/или распаковка. Однако объявление метода переменной arity (§8.4.1) может изменить метод, выбранный для вызова метода метода выражение, поскольку метод переменной arity рассматривается как фиксированный arity в первой фазе. Например, объявление m (Object...) в классе, который уже объявляет m (Object), вызывает m (Object) no более длинные выбираются для некоторых выражений вызова (например, m (null)), так как m (Объект []) более конкретный.

  1. Вторая фаза (§15.12.2.3) выполняет разрешение перегрузки, разрешая бокс и распаковку, но все же исключает использование переменной вызов метода arity. Если в течение этого затем обработка продолжается до третьей фазы....

Изменить: о выбирая float вместо double:

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

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

Первая фаза разрешения перегрузки выберет два из этих четырех методов.

private static void foo(double aDouble) 
private static void foo(float aFloat) 

потому что первая фаза не разрешает бокс/распаковку (Long), и вы не можете передать Long в метод с параметром int без явного литья. Затем выбирается наиболее конкретный метод. В этой ситуации метод float будет интерпретироваться как наиболее конкретный, чем double.

Ответ 3

У конверсии есть правила приоритета. Он выберет расширение по боксу .

Итак, в этом случае компилятор ищет метод, который может принимать параметр больше (ближе всего больше), чем длинный примитивный тип данных в качестве аргумента, который является float.

Если вы удаляете методы foo (double aDouble) и foo (float aFloat) из вашего опубликованного примера, тогда компилятор выполнит бокс и выберите foo (Long aWrapperLong)

Ответ 4

Расширение имеет приоритет перед автобоксированием для перегрузки. Классы Wrapper для примитивов (Integer, Long и т.д.) Не были введены в java с самого начала и потому, что Java поддерживает обратную совместимость, он должен выполнять код, который был написан в одной версии Java таким же образом в более новых версиях.