摘要: 多态是面向对象令人惊叹的特性.
1. 什么是多态
多态是指同一消息导致不同的行为. 也就是说, 名义上使用了同一个方法调用, 而实际上执行了不同的实际方法.
2. 如何获得多态
多态通过覆盖获得.
2.1 通过继承
先生成一个 Doctor 类.
package thinking;
public class Doctor extends Human {
/**
*********************
* Eat and grow.
*********************
*/
public void eat() {
if (weight < 70000)
weight += 15;
}// Of eat
/**
*********************
* The test method.
*********************
*/
public static void main(String args[]) {
Human zhangsan = new Programmer();
Human lisi = new Doctor();
for (int i = 0; i < 10; i++) {
zhangsan.eat();
lisi.eat();
} // Of for i
System.out.println("The current weight of zhangsan is: " + zhangsan.weight);
System.out.println("The current weight of lisi is: " + lisi.weight);
}// Of main
}// Of class Doctor
本例可以看出, zhangsan 和 lisi 在变量声明的时候, 都是 Human. 但在实例化的时候, zhangsan 被实例化为 Programmer, 而 lisi 为 Doctor.
- Programmer 并没有自己的 eat, 因此 zhangsan 按照人类的正常一般方式生长, 吃一次长 10 g, 10 次长了 100 g, 总质量 3100 g.
- Doctor 实现了自己的 eat, 它对 Human 中的 eat 进行了 覆盖 overwrite, lisi 10 次长到 3150 g.
从这里我们可以看出:
- 覆盖为同一方法产生了不同的版本, 因此它是获得多态的必要条件.
- 在声明 Human 的时候, 实例化可以是 Human 或其任何子类. 找你要一个人, 你给了一个程序猿, 这没有任何问题. 但反之不行.
2.2 接口实现
我们把 Postman 扩充一下, 增加一个 main 方法.
package thinking;
public class Postman extends Human implements Messager{
/**
*********************
* Implements the method of the interface.
*********************
*/
public void sendMessage(){
System.out.println("I'm a postman sending message.");
weight -= 50;
}//Of sendMessage
/**
*********************
* Test the class.
*********************
*/
public static void main(String args[]){
Messager zhangsan = new Postman();
Messager gugu = new Pigeon();
zhangsan.sendMessage();
gugu.sendMessage();
}//Of main
} //Of class Postman
从这里可以看出:
- 可以声明一个对象为一个接口, 如 Messager zhangsan.
- 实例化时与类的原理相同.
- 由于实现接口的类都必须写出相应的方法体, 自然就有方法的覆盖.
3. 多态还有什么神奇的事情
讲一个故事:
第 1 天, 你写了一个接口 Messager, 声明了sendMessage() 方法
第 2 天, 你写了 PostOffice 类, 它使用了 Messager 接口, 并调用了其 sendMessage() 方法.
第 3 天, 你写了 Postman 类, 实现了 sendMessage() 方法;
第 4 天, 你写了 MessageTest 类, 它生成了 PostOffice 的对象 tempOffice, 该对象利用类 Postman 的对象 zhangsan 送信.
package thinking;
public class PostOffice {
/**
* The object is claimed as an interface.
*/
Messager myMessager;
/**
*********************
* The default constructor.
*********************
*/
public PostOffice(Messager paraMessager) {
myMessager = paraMessager;
}// Of the constructor
/**
*********************
* Send.
*********************
*/
public void send() {
System.out.println("From a post office.");
myMessager.sendMessage();
}// Of send
}// Of class PostOffice
等等! 这里有点没对. 按照面向过程的思维, 后写的函数可以调用先写的函数. 但是, 第 2 天写的代码, 为什么能够调用第 3 天的代码? 它是如何做到未卜先知的?
从程序的角度, 编译时计算机看到 PostOffice 里面的语句
myMessager.sendMessage();
觉得没问题, 因为 sendMessage() 是 Messager 接口中定义好了的.
执行时, 计算机使用 Postman 里面的 sendMessage() 替换了接口中的方法, 所以完美地执行了任务.
Amazing ~
如果你熟悉人类社会的运作模式, 会觉得这个稀松平常: 老司机可以开新车, 虽然他从来没用这辆车练过手; 插座可以为新手机充电, 虽然它从来没用该手机测试过.