Ответ 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 должна быть обратно совместимой.