多态使用时的缺陷主要分两种情形
- “覆盖”
private
方法 - 调用域与静态方法
由于调用域和静态方法十分相似,先考虑调用域与“覆盖”private
方法的情形。我写了个四层继承关系的程序表现这种陷阱,如下
/*
* 继承关系:SubSubSub 继承 SubSub 继承 Sub 继承 HelloWorld
*/
public class HelloWorld {
public int field = 0;
public void f(){
System.out.println("Super.f()");
}
public static void main(String[] args) {
//4种不同的父类引用
HelloWorld fool1 = new SubSubSub();
Sub fool2 = new SubSubSub();
SubSub fool3 = new SubSubSub();
SubSubSub fool4 = new SubSubSub();
//同一子类对象对应不同引用的域
System.out.print(fool1.field + " ");
System.out.print(fool2.field + " ");
System.out.print(fool3.field + " ");
System.out.println(fool4.field + " ");
//同一子类对象对应不同引用的方法调用
fool1.f();
fool2.f();
fool3.f();
fool4.f();
}
}
class Sub extends HelloWorld{
public int field = 1;
public void f() {
System.out.print("Sub.f()" + " ");
System.out.print(field + " ");
}
}
class SubSub extends Sub{
public int field = 2;
public void f() {
System.out.print("SubSub.f()" + " ");
System.out.print(field + " ");
}
}
class SubSubSub extends SubSub{
public int field = 3;
public void f(){
System.out.print("SubSubSub.f()" + " ");
System.out.print(field + " ");
}
}
输出结果:
0 1 2 3
SubSubSub.f() 3 SubSubSub.f() 3 SubSubSub.f() 3 SubSubSub.f() 3
从运行结果可知:
- 对于域,由当前对象在继承关系中的实际引用决定。即使对象是SubSubSub类处于继承关系最下层(假设继承图中基类在顶层,子类在基类下方),若将其引用向上转型为父类引用(如fool1、fool2、fool3),那么当调用域
当前引用.field
时,输出的是父类的field值。 - 方法则不同,无论有无初始化(父类构造器中调用被子类覆盖的方法),在整个继承体系(继承类、基类)中,子类的方法都会渗透到父类最顶层。无论当前对象
new SubSubSub()
的引用是否转型为父类引用,当调用继承体系中可继承的方法时,只会调用实际对象覆盖过的子类方法,即后期绑定。
不过要注意,向上转型为父类引用调用方法时可能会遇到一个陷阱:子类”覆盖“private
方法
public class HelloWorld {
public int field = 0;
//父类私有方法
private void f(){
System.out.println("Super.f()");
}
public static void main(String[] args) {
//4种不同的父类引用
HelloWorld fool1 = new SubSubSub();
Sub fool2 = new SubSubSub();
SubSub fool3 = new SubSubSub();
SubSubSub fool4 = new SubSubSub();
//同一子类对象对应不同引用的方法调用
fool1.f();
fool2.f();
fool3.f();
fool4.f();
}
}
输出结果:
Super.f()
SubSubSub.f() 3 SubSubSub.f() 3 SubSubSub.f() 3
从运行结果可知:
- 当子类中有与基类
private
方法(如f())同名的 “覆盖”方法时,由于private
方法无法被子类继承也就无法被覆盖,当子类对象引用转型为基类引用时,无法通过基类引用实际子类“覆盖”的方法,实际调用的是基类的private
方法。因为这时父类的private
方法不在继承方法体系中,和子类同名的“覆盖”方法没有任何关系。private
方法相当于父类的独有方法,同理,子类同名的“覆盖”方法也是子类“独有”的方法。
注意:在一个继承体系中,被“覆盖”的
private
方法只能存在于基类方法中,不属于可继承方法。
总而言之,继承中覆盖只对继承体系中可继承的方法集合适用,而域独立存在于所属类,不在继承之列。
每种多态引用只能调用当前引用对应类的方法和域,跟引用实际对象(子类或基类对象,如SubSubSub类对象)的方法无关,只有继承体系内可继承的具体方法实现能调用实际对象的覆盖方法。所以实际看来,“覆盖”private
方法只是一个“看似陷阱”的陷阱。