7.2 深入理解
7.2 深入理解
对于
public static void tune(Instrument i) {
// ...
i.play(Note.middleC);
}
它接收
将一个方法调用同一个方法主体连接到一起就称为“绑定”(Binding
上述程序最令人迷惑不解的地方全与早期绑定有关,因为在只有一个
解决的方法就是“后期绑定”,它意味着绑定在运行期间进行,以对象的类型为基础。后期绑定也叫作“动态绑定”或“运行期绑定”。若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。
为什么要把一个方法声明成
知道
在面向对象的程序设计中,有一个经典的“形状”例子。由于它很容易用可视化的形式表现出来,所以经常都用它说明问题。但很不幸的是,它可能误导初学者认为
形状例子有一个基础类,名为
上溯造型可用下面这个语句简单地表现出来:
Shape s = new Circle();
在这里,我们创建了
s.draw();
同样地,大家也许认为会调用
//: Shapes.java
// Polymorphism in Java
class Shape {
void draw() {}
void erase() {}
}
class Circle extends Shape {
void draw() {
System.out.println("Circle.draw()");
}
void erase() {
System.out.println("Circle.erase()");
}
}
class Square extends Shape {
void draw() {
System.out.println("Square.draw()");
}
void erase() {
System.out.println("Square.erase()");
}
}
class Triangle extends Shape {
void draw() {
System.out.println("Triangle.draw()");
}
void erase() {
System.out.println("Triangle.erase()");
}
}
public class Shapes {
public static Shape randShape() {
switch((int)(Math.random() * 3)) {
default: // To quiet the compiler
case 0: return new Circle();
case 1: return new Square();
case 2: return new Triangle();
}
}
public static void main(String[] args) {
Shape[] s = new Shape[9];
// Fill up the array with shapes:
for(int i = 0; i < s.length; i++)
s[i] = randShape();
// Make polymorphic method calls:
for(int i = 0; i < s.length; i++)
s[i].draw();
}
} ///:~
针对从
在主类
Circle.draw()
Triangle.draw()
Circle.draw()
Circle.draw()
Circle.draw()
Square.draw()
Triangle.draw()
Square.draw()
Square.draw()
当然,由于几何形状是每次随机选择的,所以每次运行都可能有不同的结果。之所以要突出形状的随机选择,是为了让大家深刻体会这一点:为了在编译的时候发出正确的调用,编译器毋需获得任何特殊的情报。对
现在,让我们仍然返回乐器(Instrument)示例。由于存在多态性,所以可根据自己的需要向系统里加入任意多的新类型,同时毋需更改
所有这些新类都能与老类——
//: Music3.java
// An extensible program
import java.util.*;
class Instrument3 {
public void play() {
System.out.println("Instrument3.play()");
}
public String what() {
return "Instrument3";
}
public void adjust() {}
}
class Wind3 extends Instrument3 {
public void play() {
System.out.println("Wind3.play()");
}
public String what() { return "Wind3"; }
public void adjust() {}
}
class Percussion3 extends Instrument3 {
public void play() {
System.out.println("Percussion3.play()");
}
public String what() { return "Percussion3"; }
public void adjust() {}
}
class Stringed3 extends Instrument3 {
public void play() {
System.out.println("Stringed3.play()");
}
public String what() { return "Stringed3"; }
public void adjust() {}
}
class Brass3 extends Wind3 {
public void play() {
System.out.println("Brass3.play()");
}
public void adjust() {
System.out.println("Brass3.adjust()");
}
}
class Woodwind3 extends Wind3 {
public void play() {
System.out.println("Woodwind3.play()");
}
public String what() { return "Woodwind3"; }
}
public class Music3 {
// Doesn't care about type, so new types
// added to the system still work right:
static void tune(Instrument3 i) {
// ...
i.play();
}
static void tuneAll(Instrument3[] e) {
for(int i = 0; i < e.length; i++)
tune(e[i]);
}
public static void main(String[] args) {
Instrument3[] orchestra = new Instrument3[5];
int i = 0;
// Upcasting during addition to the array:
orchestra[i++] = new Wind3();
orchestra[i++] = new Percussion3();
orchestra[i++] = new Stringed3();
orchestra[i++] = new Brass3();
orchestra[i++] = new Woodwind3();
tuneAll(orchestra);
}
} ///:~
新方法是
在
可以看到,在围绕