多态基于封装和继承,是Java三大特性的重中之重。多态通过动态绑定的技术,根据引用对象的实际类型调用对应的方法,这样的特性有利于消除类与类之间的耦合关系。其应用广泛,比如那些自动生成代码的软件,一般都会有提前写好的公有代码,而如果软件使用者自定义了某项功能代码,则会优先使用自定义的代码。
实现多态的三个必要条件:继承
重写
父类引用指向子类对象
这三个条件缺一不可,继承和重写这里就不再赘述了,主要说明下父类引用指向子类对象。
一个父类的引用可以指向多个子类对象,而运行时,则由子类对象的类型来决定调用的是哪个类型的方法。当父类引用指向子类对象时,其实调用的是父类(即当前类)的方法,而根据动态绑定的技术,如果在子类中重写了某个父类方法,则会调用子类中的该方法。当然了,这个方法必须是在父类中已定义的,如果只在子类定义了某个方法,则父类的引用是无法调用的。
举个例子:
public class PolymorphicTest {
public class A{
public void show(A obj){
System.out.println("Q");
}
public void show(C obj){
System.out.println("W");
}
public void show(){
show2();
}
public void show2(){
System.out.println("A");
}
}
public class B extends A{
public void show(B obj){
System.out.println("E");
}
public void show(A obj){
System.out.println("R");
}
public void show2(){
System.out.println("S");
}
}
public class C extends B{
public void show(C obj){
System.out.println("T");
}
public void show2(){
System.out.println("D");
}
}
public static void main(String[] args) {
PolymorphicTest p = new PolymorphicTest();
A a1 = p.new A();
A a2 = p.new B();
B a3 = p.new C();
B b = p.new B();
C c = p.new C();
a2.show(c); // W a1.show(b); // Q a2.show(b); // R a3.show(c); // T a3.show(); // D }
}
以上代码输出结果如下:
为何会是这样?
实际上,这里涉及到调用方法的优先级。按如下数字序号的顺序调用:
this.show(O):优先查找当前类的show(O),找不到继续第2步;
super.show(O):往父类查找show(O),找不到继续第3步;
this.show((super)O):将参数类型O转换成其父类类型后,在当前类查找show((super)O),找不到继续第4步;
super.show((super)O):将参数类型O转换成其父类类型后,往父类查找show((super)O)。
this表示当前类型,即父类引用的类型。O表示参数类型。
接下来我们按照上面的方法优先级,逐个分析代码里那五行的调用逻辑。
a2.show(c):a2是A类引用指向子类B的对象,实参c的类型是C,会先在A类查找show(C obj)方法,在A类找到了,检查子类B是否有重写,没有重写,那么直接输出“W”;
a1.show(b):a1是A类的引用对象,并且指向了本类(非多态),实参b的类型是B,会直接在A类查找show(B obj)方法,发现A类中没有该方法,那么继续往Object类查找该方法(默认继承Object类),很显然Object类也没有,那么继续第3步,将实参类型B转换成其父类类型A,然后在A类查找show(A obj),找到了,那么直接输出“Q”;
a2.show(b):a2是A类引用指向子类B的对象,实参b的类型是B,会先在A类查找show(B obj)方法,没找到,继续往Object类查找该方法,很显然也没有,那么继续第3步,将实参类型B转换成其父类类型A,然后在A类查找show(A obj),找到了,检查子类B是否有重写,发现有重写,那么输出“Q”;
a3.show(c):a3是B类引用指向子类C的对象,实参c的类型是C,会先在B类查找show(C obj)方法,没找到,继续往其父类A找该方法,找到了,检查子类C是否有重写,发现有重写,那么输出“T”;
a3.show():a3是B类引用指向子类C的对象,无参,会先在B类查找show()方法,没找到,继续往父类A找该方法,找到了,同时A类中的该方法调用了show2()方法,show2()方法在子类C重写了,那么输出“D”。
作者:陈小挺