[再学编程思想]第八章 多态

 “多态通过分离做什么和怎么做从另一个角度将接口和实现分离开,多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序”

一.方法调用绑定

    将一个方法调用同一个方法主体关联起来被叫做绑定,若在程序执行前进行绑定叫做前期绑定。

    后期绑定:含义是在运行时根据对象的类型进行绑定,后期绑定也叫做动态绑定或者运行时绑定。

    如果一种语言想实现后期绑定,就必须具有某种机制,以便在运行时能判断对象的类型,从而调用适当的方法。

    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的,因此不能直接访问他们,其副作用是只能通过调用方法来访问,另外你可能不会对基类中的域和导出类中的域赋予相同的名字,这种做法是容易产生混淆的。 

    如果某个方法是静态的就不具备多态性。

三.构造器和类

    未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值