一句话概述多态:父类引用指向子类实现。 多态的作用是消除类型之间的耦合关系:它可以将从同一父类中导出的子类视为同一类型来处理。这样我们使用某些代码时只要考虑父类就可以。

    8.1 再论向上转型

    1. class Instrument{
    2. public void play(Note n){
    3. System.out.println("Instrument.play()");
    4. }
    5. }
    6. class Wind extends Instrument {
    7. public void play(Note n) {
    8. System.out.println("Wind.play()" + n);
    9. }
    10. }
    11. class Piano extends Instrument{
    12. @Override
    13. public void play(Note n) {
    14. System.out.println("Piano.play()"+n);;
    15. }
    16. }
    17. public class Music{
    18. //如果tune()方法里穿的参数类型是Wind ,那么加入一个Piano类型就要重写一个tune方法
    19. public static void tune(Instrument i){
    20. i.play(Note.MIDDLE_C);
    21. }
    22. public static void main(String[] args) {
    23. // Wind flute = new Wind();
    24. // tune(flute);
    25. Instrument flute = new Wind();
    26. tune(flute);
    27. }
    28. }

    wind继承的Instument,向上转型可能会使得接口变窄,但是Instument有的接口wind全都有

    8.1.1 忘记对象类型
    我们应该让编写的代码之和基类打交道。 要不然一直像上面21,22行这样的话,每加入一个子类都要像14行那样创建一个tune方法。

    8.2 转机
    java中除了static方法和final方法(private方法属于final方法)外,其他所有方法都是后期动态绑定。

    将一个方法声明为final,就告诉编译器不需要对他进行动态绑定。

    1. import java.util.Random;
    2. class Shape{
    3. public void draw(){}
    4. public void erase(){}
    5. }
    6. class Circle extends Shape{
    7. public void draw(){
    8. System.out.println("Circle.draw()");
    9. }
    10. public void erase(){
    11. System.out.println("Circle.erase");
    12. }
    13. }
    14. class Square extends Shape{
    15. public void draw(){
    16. System.out.println("Square.draw()");
    17. }
    18. public void erase(){
    19. System.out.println("Square.erase");
    20. }
    21. }
    22. class Triangle extends Shape{
    23. public void draw(){
    24. System.out.println("Triangle.draw()");
    25. }
    26. public void erase(){
    27. System.out.println("Triangle.erase");
    28. }
    29. }
    30. class RandomShapeGenerator {
    31. private Random rand = new Random(47);
    32. public Shape next(){
    33. switch (rand.nextInt(3)){
    34. default:
    35. case 0: return new Circle();
    36. case 1: return new Square();
    37. case 2: return new Triangle();
    38. }
    39. }
    40. }
    41. public class Shapes{
    42. private static RandomShapeGenerator gen = new RandomShapeGenerator();
    43. public static void main(String[] args) {
    44. Shape[] s = new Shape[6];
    45. for (int i = 0; i < s.length; i++) {
    46. s[i] = gen.next();
    47. }
    48. for (Shape shp : s) {
    49. // shp.draw();
    50. shp.erase();
    51. }
    52. }
    53. }

    无论我们在什么时候调动next()时,绝不可能知道具体类型到底是什么,因为我们总是得到一个通用的shape引用。此处只能通过运行的时候才知道具体返回的是什么类型的。

    8.2.3可扩展性

    1. class Instrument3{
    2. void play(Note n){
    3. System.out.println("instrument.play()"+n);
    4. }
    5. String what(){
    6. return "instrument";
    7. }
    8. void adjust(){
    9. System.out.println("adjusting instrument");
    10. }
    11. }
    12. class Wind3 extends Instrument3{
    13. void play(Note n){
    14. System.out.println("Wind3.play "+n);
    15. }
    16. String what(){
    17. return "Winds3";
    18. }
    19. void adjust(){
    20. System.out.println("adjust wind");
    21. }
    22. }
    23. class Percussion extends Instrument3{
    24. void play(Note n){
    25. System.out.println("Percussion.play "+n);
    26. }
    27. String what(){
    28. return "Percussion";
    29. }
    30. void adjust(){
    31. System.out.println("Percussion wind");
    32. }
    33. }
    34. class Stringed3 extends Instrument3{
    35. void play(Note n){
    36. System.out.println("Stringed3.play "+n);
    37. }
    38. String what(){
    39. return "Stringed3";
    40. }
    41. void adjust(){
    42. System.out.println("Stringed3 wind");
    43. }
    44. }
    45. class Brass3 extends Wind3{
    46. void play(Note n){
    47. System.out.println("Brass.play() "+n);
    48. }
    49. void adjust(){
    50. System.out.println("adjusting Brass");
    51. }
    52. }
    53. class Woodwind extends Wind3{
    54. void play(Note n){
    55. System.out.println("Woodwind.play()"+n);
    56. }
    57. String what(){
    58. return "woodwind";
    59. }
    60. }
    61. public class Music3 {
    62. public static void tune(Instrument3 i){
    63. i.play(Note.MIDDLE_C);
    64. }
    65. public static void tuneAll(Instrument3[] e){
    66. for (Instrument3 i:e) {
    67. tune(i);
    68. }
    69. }
    70. public static void main(String[] args) {
    71. Instrument3[] orchestra = {
    72. new Wind3(),
    73. new Percussion(),
    74. new Stringed3(),
    75. new Brass3(),
    76. new Woodwind()
    77. };
    78. tuneAll(orchestra);
    79. }
    80. }
    81. /*out
    82. Wind3.play MIDDLE_C
    83. Percussion.play MIDDLE_C
    84. Stringed3.play MIDDLE_C
    85. Brass.play() MIDDLE_C
    86. Woodwind.play()MIDDLE_C
    87. */

    8.2.4缺陷—“覆盖”私有方法

    1. public class Private{
    2. private void f(){ //缺陷:“覆盖”私有方法
    3. System.out.println("我是父类私有的f方法");
    4. }
    5. public static void main(String[] args) {
    6. Private p = new Drive();
    7. p.f();
    8. }
    9. }
    10. class Drive extends Private{
    11. void f(){
    12. System.out.println("我是子类的f方法");
    13. }
    14. }
    15. 输出:我是父类私有的f方法

    重载:只看方法名和参数列表
    重写:返回值,方法名称,参数列表与父类一致
    结论:在子类中,对于父类的private方法,名字最好不要一样

    8.2.5 缺陷:域与静态方法
    只有普通方法的调用可以是多态的

    1. class Super {
    2. public int field = 0;
    3. public int getField() { return field; }
    4. }
    5. class Sub extends Super {
    6. public int field = 1; 属性不是多态的
    7. public int getField() { return field; } 方法可以是多态的
    8. public int getSuperField() { return super.field; }
    9. }
    10. public class FieldAccess {
    11. public static void main(String[] args) {
    12. Super sup = new Sub(); // Upcast
    13. System.out.println("sup.field = " + sup.field +
    14. ", sup.getField() = " + sup.getField());
    15. Sub sub = new Sub();
    16. System.out.println("sub.field = " +
    17. sub.field + ", sub.getField() = " +
    18. sub.getField() +
    19. ", sub.getSuperField() = " +
    20. sub.getSuperField());
    21. }
    22. } /* Output:
    23. sup.field = 0, sup.getField() = 1
    24. sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
    25. *///:~

    静态方法不是多态的

    8.3 构造器和多态
    父类的构造器总是在子类的构造过程中调用的,而且按照继承层次逐渐向上链接,以便使得每个父类的构造器得以调用。因为初始化

    8.3.2 继承与清理
    销毁的顺序应该与初始化顺序相反,因为万一某个子对象要依赖与其他对象。

    1. import static net.mindview.util.Print.*;
    2. class Characteristic {
    3. private String s;
    4. Characteristic(String s) {
    5. this.s = s;
    6. print("Creating Characteristic " + s);
    7. }
    8. protected void dispose() {
    9. print("disposing Characteristic " + s);
    10. }
    11. }
    12. class Description {
    13. private String s;
    14. Description(String s) {
    15. this.s = s;
    16. print("Creating Description " + s);
    17. }
    18. protected void dispose() {
    19. print("disposing Description " + s);
    20. }
    21. }
    22. class Amphibian {
    23. private Characteristic p = new Characteristic("can live in water");
    24. private Description t = new Description("Both water and land");
    25. Amphibian() {
    26. print("Amphibian()");
    27. }
    28. protected void dispose() {
    29. print("Amphibian dispose");
    30. t.dispose();
    31. p.dispose();
    32. }
    33. }
    34. public class Frog extends Amphibian {
    35. private Characteristic p = new Characteristic("Croaks");
    36. private Description t = new Description("Eats Bugs");
    37. public Frog() { print("Frog()"); }
    38. protected void dispose() {
    39. print("Frog dispose");
    40. t.dispose();
    41. p.dispose();
    42. super.dispose();
    43. }
    44. public static void main(String[] args) {
    45. Frog frog = new Frog();
    46. print("----------------------");
    47. frog.dispose();
    48. }
    49. }
    50. 输出:
    51. Creating Characteristic can live in water
    52. Creating Description Both water and land
    53. Amphibian()
    54. Creating Characteristic Croaks
    55. Creating Description Eats Bugs
    56. Frog()
    57. ----------------------
    58. Frog dispose
    59. disposing Description Eats Bugs
    60. disposing Characteristic Croaks
    61. Amphibian dispose
    62. disposing Description Both water and land
    63. disposing Characteristic can live in water
    64. Process finished with exit code 0

    8.3.3 构造器内部多态方法的行为

    基本构造器调用的时候,导出类的构造器没有调用;但是可以在父类的构造器中调用被子类覆盖的方法

    1. class Glyph {
    2. void draw() { print("Glyph.draw()"); }
    3. Glyph() {
    4. print("Glyph() before draw()");
    5. draw();
    6. print("Glyph() after draw()");
    7. }
    8. }
    9. class RoundGlyph extends Glyph {
    10. private int radius = 1;
    11. RoundGlyph(int r) {
    12. radius = r;
    13. print("RoundGlyph.RoundGlyph(), radius = " + radius);
    14. }
    15. void draw() {
    16. print("RoundGlyph.draw(), radius = " + radius);
    17. }
    18. }
    19. public class PolyConstructors {
    20. public static void main(String[] args) {
    21. new RoundGlyph(5);
    22. }
    23. }
    24. 输出:/*
    25. Glyph() before draw()
    26. RoundGlyph.draw(), radius = 0
    27. Glyph() after draw()
    28. RoundGlyph.RoundGlyph(), radius = 5
    29. */

    编写构造器准则:避免调用其他方法。在构造器内部唯一能够安全调用的方法是父类中的final方法,因为这些方法不能够被覆盖。

    8.4 协变返回类型
    子类中被覆盖的方法 可以返回父类方法的返回类型的 某种导出类型

    1. class Grain {
    2. public String toString() { return "Grain"; }
    3. }
    4. class Wheat extends Grain {
    5. public String toString() { return "Wheat"; }
    6. }
    7. class Mill {
    8. Grain process() { return new Grain(); }
    9. }
    10. class WheatMill extends Mill {
    11. Wheat process() { return new Wheat(); }
    12. }
    13. public class CovariantReturn {
    14. public static void main(String[] args) {
    15. Mill m = new Mill();
    16. Grain g = m.process();
    17. System.out.println(g);
    18. // m = new WheatMill();
    19. // g = m.process();
    20. Mill m1 = new WheatMill();
    21. Grain g1 = m1.process();
    22. System.out.println(g1);
    23. }
    24. }
    25. /*
    26. Grain
    27. Wheat
    28. */