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()方法的所有调用都是通过动态绑定进行的。