“多态通过分离做什么和怎么做从另一个角度将接口和实现分离开,多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序”
一.方法调用绑定
将一个方法调用同一个方法主体关联起来被叫做绑定,若在程序执行前进行绑定叫做前期绑定。
后期绑定:含义是在运行时根据对象的类型进行绑定,后期绑定也叫做动态绑定或者运行时绑定。
如果一种语言想实现后期绑定,就必须具有某种机制,以便在运行时能判断对象的类型,从而调用适当的方法。
java中除了static方法和final方法之外,其他所有方法都是后期绑定,这意味着我们通常情况下不必判定是否应当进行后期绑定,因为他是会自动发生的。
为什么将某个方法声明为final呢?
前一章所说的是防止覆盖该方法,但是更重要的一点是可以有效地关闭动态绑定,或者说告诉编译器不需要对其进行动态绑定。这样可以为final方法调用生成更有效地代码。
可扩展性:因为多态机制可以根据需要对系统添加任意多的新类型,而不需要修改tune()方法。下面是一个乐器的继承图。
不需要改动tune()方法,所有的类都能与原有类一起正确运行,
//: polymorphism/music3/Music3.java
// An extensible program.
package polymorphism.music3;
import polymorphism.music.Note;
import static net.mindview.util.Print.*;
class Instrument {
void play(Note n) { print("Instrument.play() " + n); }
String what() { return "Instrument"; }
void adjust() { print("Adjusting Instrument"); }
}
class Wind extends Instrument {
void play(Note n) { print("Wind.play() " + n); }
String what() { return "Wind"; }
void adjust() { print("Adjusting Wind"); }
}
class Percussion extends Instrument {
void play(Note n) { print("Percussion.play() " + n); }
String what() { return "Percussion"; }
void adjust() { print("Adjusting Percussion"); }
}
class Stringed extends Instrument {
void play(Note n) { print("Stringed.play() " + n); }
String what() { return "Stringed"; }
void adjust() { print("Adjusting Stringed"); }
}
class Brass extends Wind {
void play(Note n) { print("Brass.play() " + n); }
void adjust() { print("Adjusting Brass"); }
}
class Woodwind extends Wind {
void play(Note n) { print("Woodwind.play() " + n); }
String what() { return "Woodwind"; }
}
public class Music3 {
// Doesn't care about type, so new types
// added to the system still work right:
public static void tune(Instrument i) {
// ...
i.play(Note.MIDDLE_C);
}
public static void tuneAll(Instrument[] e) {
for(Instrument i : e)
tune(i);
}
public static void main(String[] args) {
// Upcasting during addition to the array:
Instrument[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
}
} /* Output:
Wind.play() MIDDLE_C
Percussion.play() MIDDLE_C
Stringed.play() MIDDLE_C
Brass.play() MIDDLE_C
Woodwind.play() MIDDLE_C
*///:~
新添加的what()方法返回一个带有类描述的String引用,另一个新添加的adjust()方法则提供每种乐器的调音方法。
在main中,将某种引用置入orchestra数组会自动向上转型为Instrument。
可以看到tune()方法完全可以忽略它周围代码所发生的全部变化,依旧正常运行,这正是我们所期待的多态具有的特性。
缺陷:覆盖私有方法
只有非private的方法才是可以被覆盖的,但还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们期待的来执行,在导出类中对于基类的private方法最好采用不同的名字。
缺陷:域与静态方法
只有普通的方法调用时可以多态的,如果直接访问某个域,这个访问就将在编译期间进行解析,
//: polymorphism/FieldAccess.java
// Direct field access is determined at compile time.
class Super {
public int field = 0;
public int getField() { return field; }
}
class Sub extends Super {
public int field = 1;
public int getField() { return field; }
public int getSuperField() { return super.field; }
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub(); // Upcast
System.out.println("sup.field = " + sup.field +
", sup.getField() = " + sup.getField());
Sub sub = new Sub();
System.out.println("sub.field = " +
sub.field + ", sub.getField() = " +
sub.getField() +
", sub.getSuperField() = " +
sub.getSuperField());
}
} /* Output:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
*///:~
当sub对象转型为Super引用时,任何域访问操作都将由编译器进行解析,因此不是多态的,实际上Sub包含两个称为field的域,它自己和它super得到的。然而在引用Sub中的field时所产生的默认域并非Super版本的field域,为得到Super的field只能显示的调用super.field。
通常这种情况在实践中是不会发生的,因为通常会将所有的域都设置为private的,因此不能直接访问他们,其副作用是只能通过调用方法来访问,另外你可能不会对基类中的域和导出类中的域赋予相同的名字,这种做法是容易产生混淆的。
如果某个方法是静态的就不具备多态性。
三.构造器和类
未完待续。。。