Java重载方法的参数互为父子类时处理策略的探究
本文先对Java重载方法的参数互为父子类时的处理策略进行了查证,后续对涉及到加入重写后的处理策略进行了进一步的探究。
1.起因
老师提出了这样一个问题:
类A中有两个overload方法:
void B(Object o)
void B(Number n)
客户端程序
new A().B(Integer.valueOf(1))调用的是哪个B方法?还是说这段代码无法通过编译?
2.运行结果及原因
粗略完成后运行结果如上图,调用了参数为Number n的方法。原因其实很简单,Object类是所有类的父类,而Number类又是Byte, Double, Float, Integer, Long, Short等类的父类。在上述程序中,主程序传入的参数为Integer.valueOf(1)的返回值,也就是Integer类型,三者的关系为Object→Number→Integer。A().B(Integer.valueOf(1))在class A中没有找到类型匹配的方法,这时触发了Java自动类型提升机制,对Integer进行向上转型后匹配到了了public void B(Number n) 方法,调用后输出字符串"Num".
我们用String类进行测试,符合预期,如下左图。而如果方法参数中没有String类或它的父类,则会在静态检查阶段提示错误,如下右图。
3.再探究
如果给你如下代码,你认为运行后在控制台的输出结果是什么呢?
有小伙伴答案和我最初一样是下面三句的吗?
Obj from C
Num from C
Str fromA
它的最终输出实际是
Obj from C
Obj from C
Str fromA
package one;
class A {
public void B(Object o) {
System.out.println("Obj from A");
}
public void B(String o) {
System.out.println("Str from A");
}
}
class C extends A{
public void B(Object o) {
System.out.println("Obj from C");
}
public void B(Number n) {
System.out.println("Num from C");
}
}
public class Test{
public static void main(String[] args) {
Object t1 = null;
Number t2 = null;
String t3 = null;
A a=new C();
a.B(t1);
a.B(t2);
a.B(t3);
}
}
产生这样结果的原因是这里 a 的引用类型是 class A,但是它指向的内存是类型为 class C 的一个实例。对a进行方法调用时,调用的方法必须是类A里存在的方法,如果上述代码中删去类A里的方法public void B(Object o),那么a.B(t1); a.B(t2);两个语句都会像上张图出现静态检测错误。
Java 执行方法时,是动态匹配的,在运行时才会决定用哪个方法。我们分析a.B(t2),首先它并没有在class A中找到参数类型为Number的方法,于是对Number进行向上转型,发现与A中的public void B(Object o)匹配成功。但是a指向的是class C的内存空间,而C中对public void B(Object o)方法进行了Override,所以最后动态链接调用了类C中方法输出"Obj from C".这里我引用查证时看到的前辈的说法:
Overload 是由编译器在编译时决定的,因为 a 的声明类型为 A,所以会调用 class A中 public void B(Object o) 方法,编译类型一致。
Override 是多态,由运行时实际类决定,所以调用的是class C的方法而不是class A的方法。
这样 t2 和 t3 的执行结果都很好解释,不再赘述。
以上为授课老师提出问题后思考查证的结果,若有错误,敬请斧正。