Java中的多态
1:多态是什么
多态是同一个行为具有多个不同表现形式或形态的能力。
将经理描述成职员不只是描述这两个类之间的关系的一个简便方法。回想一下,经理类具有父类职员类的所有属性、成员和方法。这就是说,任何在Employee 上的合法操作在
Manager上也合法。如果Employee 有raiseSalary()和fire()两个方法,那么Manager 类也有。 在这种Manager继承Employee的情况下,一个Employee既可以是一个普通的Employee类,也可以是一个Manager类。也就是说下述表示都是对的:
Employee e = new Employee();
Employee e = new Manager();
从上面可以看到:同一个行为Employee具有多个不同的表现形式(既可以是一个普通的Employee类,也可以是一个Manager类),这就被称为多态。
注意:方法没有多态的说法,严格说多态是类的特性。但是也有对方法说多态的,了解一下,比如前面学到的方法覆盖称为动态多态,是一个运行时问题;方法重载称为静态多态,是一个编译时问题。
2:多态与类型
一个对象只有一个格式(是在构造时给它的)。但是,既然变量能指向不同格式的对象,那么变量就是多态性的。也就是说一个对象只有一种形式,但一个变量却有多种不同形式。 象大多数面向对象语言一样,Java 实际上允许父类类型的引用变量指向一个子类的对象。因此,可以说:
Employee e = new Manager()
使用变量e是因为,你能访问的对象部分只是Employee的一个部分;Manager的特殊部分是隐藏的。这是因为编译者应意识到,e 是一个Employee,而不是一个Manager。因而,下述情况是不允许的:
e.department = " Finance " ; //非法的,编译时会出错
可能有的人会不理解,为什么明明是new的一个Manager,却不能访问Manager的属性数据。原因在于编译的时候,变量e是一个Employee的类型,编译器并不去管运行时e指
向的具体对象是一个Employee 的对象,还是一个Manager 的对象,所以它只能访问到Employee里面定义的属性和方法。所以说编译时看数据类型。那么要想访问到Manager里面的department该怎么办呢?这就需要先对e进行强制类型转换,把它还原成为Manager类型,就可以访问到Manager里面的属性和方法了,如下:
Employee e = new Manager();
Manager m = (Manager)e;
m.department = “开发部”; //这就是合法的了
3:instanceof运算符
多态性带来了一个问题:如何判断一个变量所实际引用的对象的类型。C++使用runtime-type information(RTTI),Java使用instanceof操作符。
instanceof 运算符功能:用来判断某个实例变量是否属于某种类的类型。一旦确定了变量所引用的对象的类型后,可以将对象恢复给对应的子类变量,以获取对象的完整功能。
示例如下:
public class Employee extends Object
public class Manager extends Employee
public class Contractor extends Employee
如果通过Employee类型的引用接受一个对象,它变不变成Manager或Contractor都可以。可以象这样用instanceof 来测试:
public class Employee extends Object
public class Manager extends Employee
public class Contractor extends Employee
public void method(Employee e) {
if (e instanceof Manager) {
//如果雇员是经理,可以做的事情写在这里
}else if (e instanceof Contractor) {
//如果雇员是普通的职员,可以做的事情写在这里
}else {
//说明是临时雇员,可以做的事情写在这里
}
}
4:多态对象的类型转换
在你接收父类的一个引用时,你可以通过使用instanceof运算符判定该对象实际上是你所要的子类,并可以用类型转换该引用的办法来恢复对象的全部功能。
public void method(Employee e) {
if (e instanceof Manager) {
Manager m = (Manager)e;
System.out.println( " This is the manager of " + m.department);
}
// rest of operation
}
如果不用强制类型转换,那么引用e.department的尝试就会失败,因为编译器不能将被称做department的成员定位在Employee类中。
如果不用instanceof做测试,就会有类型转换失败的危险。通常情况下,类型转换一个对象引用的尝试是要经过几种检查的:
向上强制类型转换类层次总是允许的,而且事实上不需要强制类型转换运算符。可由简单的赋值实现。
严格讲不存在向下类型转换,其实就是强制类型转换,编译器必须满足类型转换至少是可能的这样的条件。比如,任何将Manager引用类型转换成Contractor引用的尝试是肯定不允许的,因为Contractor不是一个Manager。类型转换发生的类必须是当前引用类型的子类。
如果编译器允许类型转换,那么,该引用类型就会在运行时被检查。比如,如果instanceof 检查从源程序中被省略,而被类型转换的对象实际上不是它应被类型转换进去
的类型,那么,就会发生一个运行时错误(exception)。
5:动态绑定
绑定:将一个方法调用同一个方法主体连接到一起就称为“绑定”(Binding)。
动态绑定:当给对象发送请求时,所引起的具体操作既与请求本身有关又与接受对象有关。支持相同请求的不同对象可能对请求激发的操作有不同的实现。发送给对象的请求和它的相应操作在运行时刻的连接就称之为动态绑定(dynamic binding)。
动态绑定是指发送的请求直到运行时刻才受你的具体的实现的约束。因而,在知道任何有正确接口的对象都将接受此请求时,你可以写一个一般的程序,它期待着那些具有该特定接口的对象。进一步讲,动态绑定允许你在运行时刻彼此替换有相同接口的对象。这种可替换性就称为多态( p o l y m o r p h i s m ),它是面向对象系统中的核心概念之一。多态允许客户对象仅要求其它对象支持特定接口,除此之外对其假设几近于无。多态简化了客户的定义,使得对象间彼此独立,并可以在运行时刻动态改变它们相互的关系。
若在程序运行以前执行绑定(由编译器和链接程序,如果有的话),就叫作“早期绑定”。但是在只有一个Instrument句柄的前提下,编译器不知道具体该调用哪个方法。
解决的方法就是“后期绑定”,它意味着绑定在运行期间进行,以对象的类型为基础。后期绑定也叫作“动态绑定”或“运行期绑定”。若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。
Java 中绑定的所有方法都采用后期绑定技术,除非一个方法已被声明成final。这意味着我们通常不必决定是否应进行后期绑定――它是自动发生的。