面向对象程序的三大特性之多态性:
多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
1、向上转换:只能定义被子类覆写的方法,不能调用在子类中定义的方法。
这章主要讲解JAVA的三大特性中的“多态性”,顾名思义,多态就如上面的tips所讲的一样,就是接口的多种不同实现方式,在C语言里面,我们传递参数给函数形参的时候,如果想向函数传递的变量类型不同,那么我们就得在定义函数的时候添加多个不同变量类型的形参,传递结构体也一样,这样这个函数扩展起来就会比较麻烦,但在面向对象里面我们就可以解决这样的问题,它允许将子类类型的指针赋给父类类型的指针,它们的参数类型虽然不一样,却可以赋值并且根据不同的参数实现不同的方法。下面我们以程序的例子来进行讲解,首先先以代码了解一下多态的使用,下例实现多态的“向上转换”。
class Father {
private int money;
public int getMoney() {return money; }
public void setMoney(int money) {this.money = money; }
public void printInfo() {System.out.println("This is Father");}
}
class Son extends Father{
public void printInfo() {System.out.println("This is son");}
public void playGame(){System.out.println("This is son");}
}
class Daughter extends Father{
public void printInfo() {System.out.println("This is Father");}
}
public class Cnv {
public static void main (String args[]) {
Son son = new Son();
Daughter daughter = new Daughter();
/* 如下定义后如果子类已经复写了,就调用子类的方法,否则调用自己的方法 */
Father f = son;/* 多态-向上转换:父类对象=子类实例 */
f.printInfo();/* 会打印子类 Son 的printInfo */
//f.playGame();//会报错,因为f依旧是Father类型
f = daughter;
f.printInfo();/* 会打印子类 Daughter 的printInfo */
}
}
编译运行:
2、JAVA向下转换的例子,在进行对象的向下转换前,必须首先发生对象的向上转换.否则会编译不过.
class Father {
private int money;
public int getMoney() {return money; }
public void setMoney(int money) {this.money = money; }
public void printInfo() {System.out.println("This is Father");}
}
class Son extends Father{
public void printInfo() {System.out.println("This is son");}
public void playGame(){System.out.println("This is son");}
}
class Daughter extends Father{
public void printInfo() {System.out.println("This is Father");}
}
public class Cnv2 {
public static void main (String args[]) {
/* 向下转换:和向上转换反过来 */
/* 错误转换示例 */
Father f = new Son();
Son son = (Son)f;//Son的内容比Father多,直接这样转换有问题
/* 正确转换示例 */
Father f = new Son();//需要先实现一个“向上转换”,再来一个“向下转换”
Son son = (Son)f;/* 实质上就相当于:Son son = new Son(); */
/* 那为什么要做这么多余的转换呢?下面会慢慢讲解到 */
}
}
public class Cnv3{
public static void main(String args[]){
Father f = new Father();
Son s = new Son();
Daughter d = new Daughter();
print(f);
print(s);
print(d);
}
/* 没有多态性只能如下做 */
public static void print(Father f){
f.printInfo();
}
public static void print(Son s){
s.printInfo();
}
public static void print(Daughter d){
d.printInfo();
}
}
3、看一下,下面的例子,假如有一千个类继承了father这个类,如果我们要打印他们的信息,那样我们岂不是要写1千个print函数,下面的第二个代码则通过向上转换这个技巧实现.
上面为什么要做这么多余的转换呢,接着往下看下面的例子就明白了。首先我们先看看不使用多态的话,一般的做法事什么样的。
class Father {
private int money;
public int getMoney() {return money; }
public void setMoney(int money) {this.money = money; }
public void printInfo() {System.out.println("This is Father");}
}
class Son extends Father{
public void printInfo() {System.out.println("This is son");}
public void playGame(){System.out.println("This is son");}
}
class Daughter extends Father{
public void printInfo() {System.out.println("This is Dauhter");}
}
public class Cnv3 {
public static void main (String args[]) {
Father f = new Father();
Son s = new Son();
Daughter d = new Daughter();
print(f);
print(s);
print(d);
}
/* 没有多态性只能如下做 */
public static void print(Father f){
f.printInfo();
}
public static void print(Son s){
s.printInfo();
}
public static void print(Daughter d){
d.printInfo();
}
}
很显然,如果不使用多态,上面一次性创建了三个重载的同名方法为了满足传递进不同的参数,那如果 Father 这个父类衍生出 1000 甚至更多的子类,难道要写 1000 次这样的方法吗?显示是不可能的,这时就可以使用多态的“向上转换”了,如下代码:
通过向上转换实现:
class Father {
private int money;
public int getMoney() {return money; }
public void setMoney(int money) {this.money = money; }
public void printInfo() {System.out.println("This is Father");}
}
class Son extends Father{
public void printInfo() {System.out.println("This is son");}
public void playGame(){System.out.println("This is son");}
}
class Daughter extends Father{
public void printInfo() {System.out.println("This is Dauhter");}
}
public class Cnv4 {
public static void main (String args[]) {
Father f = new Father();
Son s = new Son();
Daughter d = new Daughter();
/* 实现跟上一程序一样的效果 */
print(f);
print(s);
print(d);
}
/* 多态性,传进来的实参可能是子类的实例 */
public static void print(Father f){
f.printInfo();
}
}
上述两份代码编译运行结果一样:
4、instanceof: 用来判断一个对象是不是某个类的实例
那还有一种情况,就是调用的这些方法都是父类或者子类继承后复写的同名方法,如果想单独只调用子类里面特有的方法怎么实现呢,这时上面说到的“向下转换”就可以使用到了,如下程序:
class Father {
private int money;
public int getMoney() {return money; }
public void setMoney(int money) {this.money = money; }
public void printInfo() {System.out.println("This is Father");}
public void drink(){System.out.println("drink");}
}
class Son extends Father{
public void printInfo() {System.out.println("This is son");}
public void playGame(){System.out.println("playGame");}
}
class Daughter extends Father{
public void printInfo() {System.out.println("This is Dauhter");}
public void dance(){System.out.println("dance");}
}
public class Cnv5{
public static void main (String args[]) {
Father f = new Father();
Son s = new Son();
Daughter d = new Daughter();
/* “向下转换”多态的实现 */
printAction(f);
printAction(s);
printAction(d);
}
/* 多态性,向下转换 */
/* 需要配合一个 instanceof 关键字来判断是否属于某个子类的实例化对象
如果是的话执行向下转换 */
/* 方法形参:Father f = d/s/d; d/s/d为传递进来的实参,都做向上转换为Father类型*/
public static void printAction(Father f){
if (f instanceof Son){//判断是否属于Son类的实例化对象
Son s = (Son)f;//前面传递进参数时做了向上转换后,这里再向下转换
s.playGame();//转换过后这里就可以直接调用子类的自己的成员方法了
}else if (f instanceof Daughter){//判断是否属于Daughter类的实例化对象
Daughter d = (Daughter)f;//向下转换到Daughter
d.dance();
}else if(f instanceof Father){
f.drink();
}
}
}
编译运行结果:
总的来说,向上转换后是父类的类型,虽然此时多态会根据形参先调用子类的同名方法,但向上转换没法实现调用子类自己的成员方法(因为此时是父类类型)。而向下转换可以,向下转换后是子类的类型,所以可以直接调用子类的成员方法,但是由于方法定义时形参为了统一,使用的是父类的类型,而我们传递的却是子类的类型,所以需要先进行一下向上转换,把子类先转为父类类型,然后再配合 instanceof 关键字来判断是否属于某个子类的实例化对象,是的话,就向下转换为对应的子类类型,以后就可以使用子类的成员方法了。