多态
多态方法调用允许一种类型表现出与其他相似类型之间的区别,只要它们都是从同一基类导出而来的。
再论向上转型
对象既可以作为它自己本身的类型使用,也可以作为它的基类型使用,这种把某个对象的引用视为对其基类型的引用的做法被称为向上转型(因为在继承树的画法中,积累是放在上方的)。
//Note.java
public enum Note {
MIDDLE_C,C_SHAPE,B_FLAT;
}
//:Instrument.java
class Instrument {
public void play(Note n) {
System.out.println("Instrument.play()");
}
}
//:Wind.java
public class Wind extends Instrument {
public void play(Note n){
System.out.println("Wind.play()"+n);
}
}
//Music.java
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的类。
转机
方法调用绑定
将一个方法调用同一个方法主体关联起来被称作绑定。
当编译器只有一个Instrument引用时,它无法知道究竟调用哪个方法才对,解决的方法就是后期绑定,其含义是在运行时根据对象的类型进行绑定
后期绑定也称动态绑定或运行时绑定
后期绑定会自动进行
产生正确的行为
向上转型还可以像下面这条语句这么简单:(Circle类由Shape类导出)
Shape s= new Circle();
这里创建了一个Circle对象,并把得到的引用立即赋值给Shape(通过继承,Circle就是一种Shape)
假设调用一个基类:
s.draw();
由于后期绑定(多态),正确调用了Circle.draw()方法。
可扩展性
根据前面的Instrument(乐器)示例:
由于有多态机制,我们可以根据自己的需要对系统添加任意多的新类型,而不需要更改tune()方法。
class Instrument {
void play(Note n) {
System.out.println("Instrument.play() " +n);
}
String what(){return "Instrument";}
void adjust() {
System.out.println("Adjusting Instrument");
}
}
class Wind extends Instrument {
void play(Note n) {
System.out.println("Wind.play() " +n);
}
String what(){return "Wind";}
void adjust() {
System.out.println("Adjusting Wind");
}
}
class Percussion extends Instrument {
void play(Note n) {
System.out.println("Percussion.play() "+n);
}
String what(){return "Percussion";}
void adjust() {
System.out.println("Adjusting Percussion");
}
}
class Stringed extends Instrument{
void play(Note n) {
System.out.println("Stringed.play() "+n);
}
String what(){return "Stringed";}
void adjust(){
System.out.println("Adjusting Stringed");
}
}
class Brass extends Wind{
void play(Note n){
System.out.println("Brass.play() "+n);
}
void adjust(){
System.out.println("Adjusting Brass");
}
}
class Woodwind extends Wind{
void play(Note n){
System.out.println("Woodwind.play() "+n);
}
void adjust(){
System.out.println("Adjusting Woodwind");
}
}
public class Music2 {
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){
Instrument[] orchestra={
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
}
}
在main()中,当我们将某种引用置入orchestra数组中,就会自动向上转型到Instrument。
缺陷:覆盖私有方法
只有非private方法才可以覆盖(在导出类中,对于基类中的private方法,最好采用不同的名字)
缺陷:域与静态方法
如果某个方法是静态的,它的行为就不具有多态性
构造器和多态
构造器不具有多态性(实际上是static方法,只不过该static声明是隐式的)
构造器的调用顺序
1.调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次的根,然后是下一层的导出类,直到最底层的导出类。
2.按声明顺序调用成员的初始化方法。
3.调用导出类构造器的主体。
协变返回类型
表示在导出类中的被覆盖方法可以返回基类方法的返回类型的某种导出类型
参考书《Java编程思想》