转机

package polymorphism.music;
//Note to play on musical instrument


public enum Note {
    MIDDLE_C,
    C_SHARP,
    B_FLAT;//Etc;

}///:~
package polymorphism.music;
//:polymorphism/music/Intrument.java

import static net.mindview.util.Print.*;


public class Instrument {
    public void play(Note n)
    {
        print("Instrument.play()");
    }

}///:~
package polymorphism.music;
//:polymorphism/music/Wind.java
import static net.mindview.util.Print.*;
//Wind object are instruments
//because they have the same interface:

public class Wind extends Instrument {
    //Redefine interface method:
    public void play(Note n){
        print("Wind.play() "+ n);
    }

}///:~
package polymorphism.music;
//:polymorphism/music/Music.java
//Inheritance & up casting 继承和向上转型

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

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Wind flute=new Wind();
        tune(flute);          //up casting向上转型了

    }

}///:~
/*output
 * Wind.play() MIDDLE_C
 */

运行这个程序后,我们便发现Music.java的难点所在。请观察一下tune()方法:

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

它接受一个Instrument引用。那么在这种情况下,编译器怎样才能知道这个Instrument引用指向的是Wind对象,而不是Brass对象或Stringed对象呢?

方法调用绑定

绑定的定义,将一个方法调用同一个方法主体关联起来
前期绑定的定义,若在程序执行前进行绑定(如果有的话,由编译器和连接程序实现)
后期绑定也称动态绑定或运行绑定,在运行时根据对象的类型进行绑定
。编译器一直不知道对象的类型,但是方法调用机制能够找到正确的方法,并加以调用。
Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。这意味着通常情况下,我们不必判定是否应该进行后期绑定——它一定会自动发生。


为什么要将某个方法声明为final呢?
正如前一章所述,它可以防止其他人覆盖方法。但重要的一点是或许是:这样做可以有效地“关闭”动态绑定,或者说,告诉编译器不需要对其进行动态绑定。这样编译器就可以final方法调用生成更有效的代码。然而,大多数情况下,这样做对程序的整体性能不会有什么改观。所以,最好根据设计来决定是否使用final,而不是出于试图提高性能的目的来使用final。

产生正确的行为

一旦知道Java中所有方法都是通过动态绑定实现多态这个事实之后,我们就可以编写只与基类打交道的程序代码了,并且这些代码对所有的导出类都可以正确运行。或者换种说法,发送消息给某个对象,让该对象去断定应该做什么事。
面向对象程序设计中,有一个经典的例子就是“几何形状”(shape).

基类Shape.java:

package polymorphism.shape;
//:polymorphism/shape/Shape.java

public class Shape {
    public void draw(){

    }
    public void erase(){

    }

}///:~

导出类Circle.java:

package polymorphism.shape;
//:polymorphism/shape/Circle.java

import static net.mindview.util.Print.*;
public class Circle extends Shape {
    public void draw(){
        print("Circle.draw()");
    }

    public void erase(){
        print("Circle.erase()");
    }

}///:~

导出类Square.java:

package polymorphism.shape;
//:polymorphism/shape/Square.java

import static net.mindview.util.Print.*;

public class Square extends Shape {
    public void draw(){
        print("Square.draw()");
    }

    public void erase(){
        print("Square.erase()");
    }

}///:~

导出类Triangle.java:

package polymorphism.shape;
//:polymorphism/shape/Triangle.java
import static net.mindview.util.Print.*;

public class Triangle extends Shape {
    public void draw(){
        print("Triangle.draw()");
    }

    public void erase(){
        print("Triangle.erase()");
    }

}///:~

RandomShapeGenerator是一种“工厂”(factory),在我们每次调用next()方法时,它可以随机选择的Shape对象产生一个引用。请注意向上转型是在return语句里发生的。每个return语句取得一个指向某个Circle、Square、或者Triangle的引用。
随机选择几何形状是为了让大家理解:在编译时,编译器不需要获得任何特殊信息就能进行正确的调用。对draw()方法的所有调用都是通过动态绑定进行的。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值