java中的方法重载解析
Posted
技术标签:
【中文标题】java中的方法重载解析【英文标题】:Method overload resolution in java 【发布时间】:2015-07-18 12:11:26 【问题描述】:以下是我对 java 中重载解析的了解:
编译器试图解析给定方法调用的过程 重载的方法定义称为重载决议。如果 编译器找不到它寻找最接近匹配的精确匹配 仅使用向上转换(向下转换永远不会完成)。
这是一个类:
public class MyTest
public static void main(String[] args)
MyTest test = new MyTest();
Integer i = 9;
test.TestOverLoad(i);
void TestOverLoad(int a)
System.out.println(8);
void TestOverLoad(Object a)
System.out.println(10);
正如预期的那样,输出是 10。
但是,如果我稍微更改类定义并更改第二个重载方法。
public class MyTest
public static void main(String[] args)
MyTest test = new MyTest();
Integer i = 9;
test.TestOverLoad(i);
void TestOverLoad(int a)
System.out.println(8);
void TestOverLoad(String a)
System.out.println(10);
输出为 8。
在这里我很困惑。如果从不使用向下转换,那么为什么要打印 8 呢?为什么编译器选择了TestOverLoad
方法,它以int
作为参数,从Integer
向下转换为int
?
【问题讨论】:
Integer
-> int
不是低调——它是拆箱,这是一个单独的机制。
因为足够接近将Integer
拆箱到int
。 Java 从 Java 5 开始自动执行此操作。“自动装箱是 Java 编译器在基本类型及其相应的对象包装类之间进行的自动转换。例如,将 int 转换为 Integer,将 double 转换为 Double,以及以此类推。如果转换以其他方式进行,则称为拆箱。”
【参考方案1】:
编译器不会考虑向下转换,而是考虑重载解析的拆箱转换。在这里,Integer
i
将成功拆箱为int
。不考虑String
方法,因为Integer
不能扩大到String
。唯一可能的重载是考虑拆箱的重载,因此会打印8
。
第一个代码的输出是10
的原因是编译器会在取消装箱转换时考虑扩大引用转换(Integer
到Object
)。
Section 15.12.2 of the JLS,在考虑哪些方法适用时,声明:
第一阶段(第 15.12.2.2 节)执行重载解决方案,不允许装箱或拆箱转换,或使用可变参数方法调用。如果在此阶段未找到适用的方法,则处理将继续到第二阶段。
第二阶段 (§15.12.2.3) 执行重载解决方案,同时允许装箱和拆箱 [...]
【讨论】:
【参考方案2】:在 Java 中,在方法重载的情况下解析方法按以下优先级完成:
1.加宽 2. 自动装箱 3. 可变参数
java 编译器认为扩大原始参数比执行自动装箱操作更可取。
换句话说,由于在 Java 5 中引入了 自动装箱,编译器会先选择较旧的样式(widening),然后再选择较新的样式(自动装箱),保持现有代码更健壮。 var-args 也是如此。
在您的第一个代码 sn-p 中,发生了引用变量的扩展,即
Integer
到Object
,而不是取消装箱,即Integer
到int
。在您的第二次 sn-p 中,不能从Integer
扩大到String
,因此会发生拆箱。
考虑以下证明上述所有陈述的程序:
class MethodOverloading
static void go(Long x)
System.out.print("Long ");
static void go(double x)
System.out.print("double ");
static void go(Double x)
System.out.print("Double ");
static void go(int x, int y)
System.out.print("int,int ");
static void go(byte... x)
System.out.print("byte... ");
static void go(Long x, Long y)
System.out.print("Long,Long ");
static void go(long... x)
System.out.print("long... ");
public static void main(String[] args)
byte b = 5;
short s = 5;
long l = 5;
float f = 5.0f;
// widening beats autoboxing
go(b);
go(s);
go(l);
go(f);
// widening beats var-args
go(b, b);
// auto-boxing beats var-args
go(l, l);
输出是:
double double double int,int Long,Long
仅供参考,这是我的blog on method overloading in Java。
P.S:我的答案是 SCJP 中给出的示例的修改版本。
【讨论】:
【参考方案3】:加宽击败拳击,拳击击败 var-args。在您的示例中,不会发生扩大,因此对其应用的装箱和 Integer 未装箱。没什么特别的。
【讨论】:
【参考方案4】:实际上在第二个示例中没有发生向下转换。发生了以下事情-
1. 整数被解包/拆箱为基本类型int
。2. 然后调用TestOverLoad(int a)
方法。
在 main 方法中你声明 Integer 像 -
Integer i = 9;
然后调用 -
test.TestOverLoad(i);
然而,您有 2 个重载版本的 TestOverLoad()
-
TestOverLoad(int a);
TestOverLoad(String a);
这里TestOverLoad()
的第二个重载版本采用完全不同的参数String
。这就是为什么 Integer
i
被拆箱为原始类型 int
并在此之后调用第一个重载版本。
【讨论】:
【参考方案5】:Java 中的所有对象都扩展了 Object 类,包括 Integer 类。这两个类具有以下关系: Integer "is a(n)" Object 因为 Integer extends Object。在您的第一个示例中,使用了带有 Object 参数的方法。
在第二个示例中,没有找到接受整数的方法。在这种情况下,Java 使用所谓的自动拆箱将 Integer 包装类解析为原始 int。因此,使用带int参数的方法。
【讨论】:
【参考方案6】:虽然@rgettman 的公认答案导致了非常正确的来源,但更准确地说,JLS Section 15.12.2 的 §15.12.2.2 和 §15.12.2.3 讨论了适用性,而不是决议 - OP 的要求。在示例中,OP 提供的两种testOverLoad
方法都是适用,即将在没有另一个的情况下成功解决。
相反,15.12.2.5. Choosing the Most Specific Method 讨论了适用方法的分辨率。
上面写着:
一种适用的方法 m1 比另一种适用的方法更具体 方法 m2,用于使用参数表达式 e1、...、ek、if 的调用 以下任何一项为真: ...
m1 和 m2 可通过严格或松散调用适用,其中 m1 具有形参类型 S1、...、Sn 和 m2 具有形参 类型 T1, ..., Tn,类型 Si 比 Ti 更具体的参数 ei 代表所有 i (1 ≤ i ≤ n, n = k)。
因此,在 OP 提供的第一个示例中,对于 Integer
类型的参数 i
方法 testOverLoad(Object a)
比 testOverLoad(int a)
更具体。
【讨论】:
以上是关于java中的方法重载解析的主要内容,如果未能解决你的问题,请参考以下文章