java 多态(一)

在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征。

多态通过分离做什么和怎么做,从另一个角度将接口和实现分离开来。多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序---即无论在项目最初创建时还是在需要添加新功能时都可以“生长”的程序。

“封装”通过合并特征和行为来创建新的数据类型。“实现隐藏”则通过将细节“私有化”把接口和实现分离开来。这种类型的组织机制对那些拥有过程化程序设计背景的人来说,更容易理解。而多态的作用则是消除类型之间的耦合关系。继承允许将对象视为它自己本身的类型或者基类型加以处理。这种能力极为重要,因为它允许将多钟类型(从同一基类导出的)视为同一类型来处理,而同一份代码也就可以毫无差别地运行在这些不同类型之上了。多态方法调用允许一种类型表现出与其他相似类型之间的区别,只要它们都是从同一基类导出而来的。这种区别是根据方法行为的不同而表示出来的,虽然这些方法都可以通过同一基类来调用。


1.1 再论向上转型

对象既可以作为它自己本身的类型使用,也可以作为它的基类型使用。而这种把某个对象的引用视为对其基类型的引用的做法陈作向上转型。

但是,这样做也有一个问题,具体看下面这个有关乐器的例子。

public enum Note {
MIDDLE_C, C_SHARP, B_FLAT;
}

public class Instrument {
public void paly(Note n){
System.out.println("Instrument.play()");
}
}

public class Wind extends Instrument{
public void play(Note n){
System.out.println("Wind.play()"+n);
}
}

public class Music {
public static void tune(Instrument i){
i.play(Note.MIDDLE_C);
}

public static void main(String[] args){
Wind flute = new Wind();
tune(flute);
}
}

Music.tune()方法接受一个Instrument引用,同时也接受任何导出自Instrument的类。在main()方法中,当一个Wind引用传递到tune()方法时,就会出现这种情况,而不需要任何类型转换。这样做事允许的---因为Wind从Instrument继承而来,所以Instrument的接口必定存在于wind中。从Wind向上转型到instrument可能会缩小接口,但不会比instrument的全部接口更窄。


1.1.1 忘记对象类型

Music.java看起来似乎有些奇怪。为什么所有人都故意忘记对象的类型呢?在进行向上转型时,就会产生这种情况;并且如果让tune()方法直接接受一个Wind引用作为自己的参数,似乎会更为直观。但这样引发的一个重要问题是:如果那样做,就需要为系统内Instrument的每种类型都编写一个新的tune()方法。这意味着在开始时就需要更多的编程,这也意味着如果以后想添加类似tune()的新方法或者添加自Instrument导出的新类,仍需要大量的工作。

如果我们只写这样一个简单的方法,它仅接受基类作为参数,而不是那些特殊的导出类。也就是说我们不管导出类的存在,编写的代码只与基类打交道,会不会更好呢?

这正是多态所允许的。


1.2 转机

运行这个程序后,我们便会发现Music.java的难点所在。Wind.play()方法将产生输出结果。这无疑是我们期望的结果。但它看起来似乎没有什么意义。请观察一下tune()方法:

public static void tune(Instrument i){

i.play(Note.MIDDLE_C);

}

它接受一个instrument引用。那么在这种情况下,编译器怎样才知道这个instrument引用指向的是wind对象,而不是其他对象呢。实际上,编译器无法得知。为了深入理解这个问题,有必要研究一个绑定这个话题。

1.2.1  方法调用绑定

将一个方法调用同一个方法主体关联起来被称作绑定。若在程序执行前进行绑定(如果有的话,由编译器和连接程序实现)叫做前期绑定。

上述程序之所以令人迷惑,主要是因为前期绑定。因为,当编译器只有一个instrument引用时,它无法知道究竟调用哪个方法才对。

解决的办法就是后期绑定,它的含义就是在运行时根据对象的类型 进行绑定。后期绑定也叫做动态绑定或运行时绑定。如果一种语言想实现后期绑定,就必须具有某种机制,以便在运行时能判断对象的类型,从而调用恰当的方法。也就是说,编译器一直不知道对象的类型,但是方法调用机制能找到正确的方法,并加以调用。后期绑定机制随编程语言的不同而有所不同,但是只要想一下就会得知,不管怎样都必须在对象中安置某种“类型信息”。

Java中除了Static方法和final方法之后,其他所有的方法都是后期绑定。这意味着通常情况下,我们不必判定是否应该进行后期绑定--它会自动发生。


1.2.2 可扩展性

由于有多态机制,我们可根据自己的需要对系统添加任意多的新类型,而不需要更改tune()方法。在一个设计良好的oop程序中,大多数或者所有的方法都会遵循tune()的模型,而且只与基类接口通信。这样的程序是可扩展的,因为可以从通用的基类继承出新的数据类型,从而新添一些功能。那些操纵基类接口的方法不需要任何改动就可以应用于新类。

tune()方法完全可以忽略它周围代码所发生的全部变化,依旧正常运行。这正是我们期望多态所具有的特征。我们所做的代码修改,不会对程序中其他不受影响的部分产生破坏。换句话说,多态是一项让程序员“将改变的事物与未变的事物分离开来”的重要技术。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值