突然想起以前遇到的一道笔试题,题目大概是这样子的
// 父类
public class Father {
}
// 子类
public class Son extends Father {
}
// 调用方
public class Main {
private static void run(Father father) {
System.out.println("Father");
}
private static void run(Son son) {
System.out.println("Son");
}
public static void main(String[] args) {
Father son = new Son();
run(son);
}
}
问:以上代码的运行结果是什么?
我们先把这个题目放一边,现在把这个问题扩展一下
public class Main {
// 重载1
private static void run(int param) {
System.out.println("int");
}
// 重载2
private static void run(long param) {
System.out.println("long");
}
// 重载3
private static void run(Integer param) {
System.out.println("Integer");
}
// 重载4
private static void run(Long param) {
System.out.println("Long");
}
// 重载5
private static void run(float param) {
System.out.println("float");
}
// 重载6
private static void run(double param) {
System.out.println("double");
}
// 重载7
private static void run(Object param) {
System.out.println("Object");
}
// 重载8
private static void run(int... param) {
System.out.println("int...");
}
public static void main(String[] args) {
run(1);
}
}
- 以上的代码运行结果是什么?
- 如果把重载1方法注释掉,运行结果是什么?
- 如果把重载1、2、3、4这4个方法注释掉,运行结果是什么?
以上的8个重载的方法,在调用者看来,如果传入一个“范围”较小的参数,比如传入参数1,好像所有方法在单独情况下都可以调用,但是在所有方法都存在的情况下,又不知道要怎么选择。调用重载方法遵循的优先级从高到低如下:
(1)精确匹配
比如传入了参数1,默认情况1是int类型的数据,那么就会调用run(int param)方法,这个没有什么疑问。而一开始的面试题也是这种情况,变量son是一个Father类型的对象,会调用run(Father father)方法从而运行结果是Father。
(2)如果是基本类型(8种),向上转为更大范围
我们都知道一个int类型的数据和一个long类型的数据运算,会先把int类型的数据隐式转换为long类型,再进行运行,结果得到一个long类型;一个long类型和一个float类型做运算,long类型会先转换为float类型;一个float类型和一个double类型运算,float类型会先转换为double型。重载方法也是同样的道理,如果没有精确的类型,会向上找,如果把重载1方法注释掉之后,会调用run(long param),如果也没有重载2,就会调用run(float param),以此类推。
(3)通过自动拆箱和装箱
假如重载1、2、5、6都被注释了,就会找自动装箱的参数,int自动装箱是Integer,也就是会调用run(Integer param)。
(4)通过子类向上继承路线依次匹配
比如以上的例子注释掉重载1、2、3、5、6之后,调用的是run(Object param),而不是run(Long param),因为Long不是Integer的父类。
(5)通过可变参数匹配
如果1、2、3、4条规则都没法匹配到方法,就会匹配可变参数,可变参数的竞争优先级是很低的。比如以上例子把重载1、2、3、5、6、7都注释掉,就会调用run(int... param)方法。
再看另一个例子
public class Main {
private static run(int param1, Integer param2) {}
private static run(Integer param1, int param2) {}
public static void main(String[] args) {
run(1, 2);
}
}
以上代码的运行结果是什么?
其实,上面的代码会编译报错,因为编译器不知道调用的时候到底是要把1自动装箱还是要把2自动装箱,调用的时候需要显式传入Integer类型的对象,而不是让编译器进行自动装箱操作,比如调用第一个方法时可以用run(1, Integer.valueOf(2)),同理,想要调用方法2时用run(Integer.valueOf(1), 2)。
说了那么多其实我们了解就行了,建议在实际的开发中尽量避免出现这样的重载方法,可以根据参数的不同改用不一样的方法名,因为这种写法其实和i++ + ++i一样会使得代码的可读性极差。