今天我们来聊聊在哪些场景下,多态是无法生效的。
如果对多态的介绍感兴趣,可查看上一篇文章:什么是多态?
下面直接进入正题,以下场景多态是无法实现的:
1.私有方法不能实现多态
public class Water {
private void flow() {
System.out.println("water flow");
}
public static void main(String[] args) {
Water water = new River();
water.flow();
}
}
public class River extends Water {
public void flow() {
System.out.println("river flow");
}
}
water flow
很显然,输出结果和预想中的不一致。private 方法默认是 final 的,final 方法不能被重写,对导出类 River 来说是屏蔽的,多态的实现依赖于方法重载,故多态在这里便失效了。在导出类中也应当避免与基类中的 private 方法同名,避免造成歧义。
2.域不适用多态
public class Water {
public int speed = 10;
}
public class River extends Water {
public int speed = 15;
public int getSuperSpeed() {
return super.speed;
}
public static void main(String[] args) {
Water water = new River();
System.out.println(" speed == " + water.speed);
River river = new River();
System.out.println(" speed == " + river.speed + " super.speed == " + river.getSuperSpeed());
}
}
speed == 10
speed == 15 super.speed == 10
上述例子证明了多态在域中也无法实现。导出类在转换为基类时,任何域的操作访问的都是基类的存储空间。而导出类不仅拥有自身域的存储空间,还保留了基类的存储空间,哪怕字段名称一样,也是不同的存储空间,例子中第二个输出结果证明了此结论。
3.静态方法不适用多态
public class Water {
public static void getName() {
System.out.println("I am water");
}
}
public class River extends Water {
public static void getName() {
System.out.println("I am river");
}
public static void main(String[] args) {
Water water = new River();
water.getName();
River river = new River();
river.getName();
}
}
I am water
I am river
这个例子较为容易理解,因静态方法是直接与类进行绑定,而非与单个对象相关联,故多态也无法生效。
接下来,我们再来看看构造器中的多态行为。假设在基类构造器中调用多态方法,在导出类中会产生什么现象呢?
public class Water {
public Water() {
getName();
}
public void getName() {
System.out.println("I am water");
}
}
public class River extends Water {
public int speed;
public void getName() {
System.out.println("I am river ,speed is " + speed);
}
public River(int speed) {
this.speed = speed;
System.out.println("river speed is " + speed);
}
public static void main(String[] args) {
River river = new River(15);
}
}
I am river ,speed is 0
river speed is 15
构造器默认是隐式的 static 方法,但确可以适用于多态。这里我们发现基类构造器调用 getName() 方法时,river 对象中的成员变量尚未完成初始化,直到导出类的构造方法调用时才完成初始化。
那么构造器中的初始化顺序究竟是怎么的呢?它们遵循以下顺序:
1.调用基类构造器,随后不断递归调用导出类的构造器,直到遇见最底层的导出类便停止递归(此时还不会调用最底层导出类的构造方法)
2.按声明顺序调用成员的初始化方法
3.调用最底层导出类构造器
这么做的原因是:必须确保基类的所有成员在任何导出类中都是有效的,所以必须优先调用基类的构造器。