当没有接受long的方法时,为什么java将long参数提升为float/double?

Rom*_*man 31 java overloading type-conversion

这是一个SSCCE,它演示了描述(恕我直言,怪异)的行为:

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
   }
} 
Run Code Online (Sandbox Code Playgroud)

为什么java会这样做?

Tom*_*icz 31

Java语言规范非常明确(强调我的):

15.12.2编译时步骤2:确定方法签名

[...]

  1. 第一阶段(§15.12.2.2)执行重载解析而不允许装箱或拆箱转换[...]如果在此阶段没有找到适用的方法,则处理继续到第二阶段.[...]

  2. 第二阶段(§15.12.2.3)执行重载解析,同时允许装箱和拆箱 [...]

  3. 第三阶段(§15.12.2.4)允许重载与变量arity方法,装箱和拆箱相结合.

也就是说,仅在第一步中print(int)并且print(float)可以是适当的.后者匹配,没有进一步调查.


这些规则的原因也在JLS中解释:

这保证了在Java SE 5.0之前在Java编程语言中有效的任何调用都不会因为引入变量arity方法,隐式装箱和/或取消装箱而被认为是不明确的.

想象一下,您的Test类是针对Java 1.4编译的(在自动装箱之前).在这种情况下,它很清楚:print(float)必须选择(假设我们同意,为什么longfloat被认为是安全的,可以是隐式的...),因为print(Long)是完全不兼容long的论点.

稍后您将针对Java 5+编译相同的代码.编译器可以:

  • 在这种情况下选择print(Long)更" 明显 ".因此在升级到Java 5后,您的程序行为有所不同......

  • 由于调用不明确,会产生编译错误.因此,以前正确的代码不再在Java 5下编译(AFAIR永远不会这样)

  • ...或保留旧语义并调用与Java 1.4相同的方法

您现在应该了解使用的原因print(float)- 因为它将在Java 1.4下选择.Java必须向后兼容.


Pet*_*rey 12

它选择的原因floatLong在于,自动装箱,后来加入和向后兼容性的原因,就必须做出同样的调用它总是这样.


Tom*_*ine 7

Tomasz Nurkiewicz指出了规范的相关部分(Java SE 7 JLS中的15.12.2),但为什么这样做呢?对于针对1.4及更早版本的向后兼容性源代码,应继续调用相同的重载方法.因此,必须忽略1.5的特性,并且只有在代码不能编译的情况下才应考虑自动装箱.

至于为何从转换longfloat可能是隐含的-这只是一个可疑的设计选择.


MrS*_*h42 5

查看文档

第 5 章转换和促销

5.1.2. 扩大原始转换

原始类型的 19 种特定转换称为扩展原始类型转换:

  • 字节到 short、int、long、float 或 double
  • 短到 int、long、float 或 double
  • char 到 int、long、float 或 double
  • int 到 long、float 或 double
  • long 浮动或加倍
  • 浮动到两倍

所以转换形式longfloat是有规则的。