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

Почему java поддерживает длинный параметр для float/double, когда нет метода, который принимает long?

Здесь SSCCE, который демонстрирует описанное поведение (IMHO, weird):

public class Test {

   public static void print(int param) {
       System.out.println("int");
   }

   public static void print(float param) {
       System.out.println("float");
   }

   public static void print(Long param) { //<--Wrapper type
       System.out.println("Long");
   }
   public static void main(String[] args) {
       long param = 100L;
       print(param);  // output == float
   }
} 

Почему Java это делает?

4b9b3361

Ответ 1

Спецификация Java Language довольно понятна в этом (выделение мое):

15.12.2 Время компиляции Шаг 2: Определение сигнатуры метода

[...]

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

  • Вторая фаза (§15.12.2.3) выполняет разрешение перегрузки, а позволяет бокс и распаковка [...]

  • Третий этап (§15.12.2.4) позволяет комбинировать перегрузку с переменной методы arity, бокс и unboxing.

То есть, на первом этапе могут быть только print(int) и print(float). Последний матч и дальнейшее расследование не проводятся.


Причина таких правил объясняется также в JLS:

Это гарантирует, что любые вызовы, которые были действительны на языке программирования Java до Java SE 5.0, не считаются неоднозначными в результате внедрения методов переменной arity, неявного бокса и/или распаковки.

Представьте, что ваш класс Test был скомпилирован против Java 1.4 (до автобоксинга). В этом случае ясно: print(float) должен быть выбран (если мы согласны, почему long - float считается безопасным и может быть неявным...), поскольку print(Long) полностью несовместим с аргументом long.

Позже вы скомпилируете тот же код с Java 5+. Компилятор может:

  • выберите print(Long) как более "очевидный" в этом контексте. Таким образом, после перехода на Java 5 ваша программа ведет себя по-другому...

  • Ошибка компиляции, поскольку вызов неоднозначен. Таким образом, ранее правильный код больше не компилируется под Java 5 (какой AFAIR никогда не бывает)

  • ... или сохранить старую семантику и вызвать тот же метод, что и в Java 1.4

Теперь вы должны понять, почему используется print(float) - потому что он был бы выбран в Java 1.4. И Java должна быть обратно совместимой.

Ответ 2

Причина, по которой он выбирает float over Long, заключается в том, что автобоксинг был добавлен позже, а для соображений обратной совместимости это должно было сделать тот же самый вызов, который он всегда делал.

Ответ 3

Томаш Нуркевич указывает на соответствующую часть спецификации (15.12.2 в Java SE 7 JLS), но зачем это делать? Для обратной совместимости исходный код, который нацелен на 1.4 и более ранние версии, должен продолжать вызывать один и тот же перегруженный метод. Поэтому функции 1,5 должны игнорироваться, и только если код не будет компилироваться иначе, следует учитывать автобоксирование.

Что касается того, почему преобразование из long в float может быть неявным - это просто спорный выбор дизайна.

Ответ 4

См. документацию

Глава 5. Конверсии и рекламные акции

5.1.2. Расширение примитивного преобразования

19 конкретных преобразований на примитивных типах называются расширяющимися примитивные преобразования:

  • byte для коротких, int, long, float или double
  • short для int, long, float или double
  • char для int, long, float или double
  • int для long, float или double
  • долго плавать или удваивать
  • float для двойного

Итак, форма преобразования long to float совпадает с правилами.