多态的概念
一个对象变量可以指示多种实际类型的现象被称为多态。
运行时多态(性):程序中定义的引用变量所指向的具体类型和通过该引用变量调用的方法在程序运行期间才会被确定。这样可以在不修改代码的情况下,改变运行时引用变量绑定的具体对象,让程序可以选择多个运行状态。
在运行期间判断所引用对象的实际类型,根据其实际类型调用其相应的方法的现象称为动态绑定。
对于面向对象而已,多态分为编译时多态和运行时多态。其中编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编译之后会变成不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。
静态类型和动态类型
在静态类型面向对象语言中,一个变量所存储的值的类型并不等同于这个变量所声明的类型。声明为父类类型的变量可以包含子类的实例值。
静态类型是指变量在声明时所确定的类型,并且一经声明就不会改变;动态类型是指这个变量实际存储的值的类型。在静态类型的面向对象程序设计语言中,在编译时消息传递表达式的合法性不是基于接收器的动态类型,而是基于接收器的静态类型。而变量对消息的响应取决于对象引用变量的动态类型。在继承链中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。即编译时先按照优先级判断消息传递表达式的合法性,到运行期,根据对象的类型调用方法。(看上述那个合法的方法是否在对象对应的类中被重写过)。
public class A{
public String show(A obj) {
return ("A");
}
public static void main(String[] args) {
B b= new B();
A a= new A();
System.out.println(b.show(a)); //(1)
}
}
class B extends A{
public String show(B obj){
return ("B");
}
}
Ouput:
A
(1)正确,编译时检查b.show(a)的合法性,而b继承了a的方法public String show(A obj),故合法
class A{
public int x;
public void f1() {
}
public void f2() {
}
}
class B extends A{
public int y;
public void f1() {
}
public void f3() {
}
}
public class Test{
public static void main(String[] args){
A ob=new A(); //(1)
ob=new B(); //(2)
ob.x=1; //(3) 正确
ob.y=2; //(4)
ob.f1(); //(5) 正确,调用子类的f1(),因为对于消息的响应取决于变量的动态类型
ob.f2(); //(6) 正确,调用父类的f2()
ob.f3(); //(7)
}
}
(1)中变量ob的静态类型是A,动态类型是A;
(2)中变量ob的静态类型是A,动态类型是B;
(4)(7)错误,因为对于引用的合法性依赖于变量的静态类型,属性y和方法f3()在A中不存在;
向上转型的特点
指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的。而对于被子类重写了的父类中的某些方法,在调用这些方法时,必须使用在子类中重写的这些方法(动态连接、动态调用)。
多态实现形式
在Java中有两种形式可以实现(体现)多态。继承和接口。
基于继承的实现机制主要表现在继承某父类的一个或多个子类对该父类某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为。对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,执行相同动作(吃)产生的行为(拿起筷子,勺子)也就不同。
通过实现接口并覆盖接口中方法的几个不同的类实现多态。在接口的多态中,接口类型的引用变量必须指向实现了该接口的类的对象,在运行时,根据对象引用的实际类型来执行对应的方法。