一句话概述多态:父类引用指向子类实现。 多态的作用是消除类型之间的耦合关系:它可以将从同一父类中导出的子类视为同一类型来处理。这样我们使用某些代码时只要考虑父类就可以。
8.1 再论向上转型
class Instrument{public void play(Note n){System.out.println("Instrument.play()");}}class Wind extends Instrument {public void play(Note n) {System.out.println("Wind.play()" + n);}}class Piano extends Instrument{@Overridepublic void play(Note n) {System.out.println("Piano.play()"+n);;}}public class Music{//如果tune()方法里穿的参数类型是Wind ,那么加入一个Piano类型就要重写一个tune方法public static void tune(Instrument i){i.play(Note.MIDDLE_C);}public static void main(String[] args) {// Wind flute = new Wind();// tune(flute);Instrument flute = new Wind();tune(flute);}}
wind继承的Instument,向上转型可能会使得接口变窄,但是Instument有的接口wind全都有
8.1.1 忘记对象类型
我们应该让编写的代码之和基类打交道。 要不然一直像上面21,22行这样的话,每加入一个子类都要像14行那样创建一个tune方法。
8.2 转机
java中除了static方法和final方法(private方法属于final方法)外,其他所有方法都是后期动态绑定。
将一个方法声明为final,就告诉编译器不需要对他进行动态绑定。
import java.util.Random;class Shape{public void draw(){}public void erase(){}}class Circle extends Shape{public void draw(){System.out.println("Circle.draw()");}public void erase(){System.out.println("Circle.erase");}}class Square extends Shape{public void draw(){System.out.println("Square.draw()");}public void erase(){System.out.println("Square.erase");}}class Triangle extends Shape{public void draw(){System.out.println("Triangle.draw()");}public void erase(){System.out.println("Triangle.erase");}}class RandomShapeGenerator {private Random rand = new Random(47);public Shape next(){switch (rand.nextInt(3)){default:case 0: return new Circle();case 1: return new Square();case 2: return new Triangle();}}}public class Shapes{private static RandomShapeGenerator gen = new RandomShapeGenerator();public static void main(String[] args) {Shape[] s = new Shape[6];for (int i = 0; i < s.length; i++) {s[i] = gen.next();}for (Shape shp : s) {// shp.draw();shp.erase();}}}
无论我们在什么时候调动next()时,绝不可能知道具体类型到底是什么,因为我们总是得到一个通用的shape引用。此处只能通过运行的时候才知道具体返回的是什么类型的。
8.2.3可扩展性
class Instrument3{void play(Note n){System.out.println("instrument.play()"+n);}String what(){return "instrument";}void adjust(){System.out.println("adjusting instrument");}}class Wind3 extends Instrument3{void play(Note n){System.out.println("Wind3.play "+n);}String what(){return "Winds3";}void adjust(){System.out.println("adjust wind");}}class Percussion extends Instrument3{void play(Note n){System.out.println("Percussion.play "+n);}String what(){return "Percussion";}void adjust(){System.out.println("Percussion wind");}}class Stringed3 extends Instrument3{void play(Note n){System.out.println("Stringed3.play "+n);}String what(){return "Stringed3";}void adjust(){System.out.println("Stringed3 wind");}}class Brass3 extends Wind3{void play(Note n){System.out.println("Brass.play() "+n);}void adjust(){System.out.println("adjusting Brass");}}class Woodwind extends Wind3{void play(Note n){System.out.println("Woodwind.play()"+n);}String what(){return "woodwind";}}public class Music3 {public static void tune(Instrument3 i){i.play(Note.MIDDLE_C);}public static void tuneAll(Instrument3[] e){for (Instrument3 i:e) {tune(i);}}public static void main(String[] args) {Instrument3[] orchestra = {new Wind3(),new Percussion(),new Stringed3(),new Brass3(),new Woodwind()};tuneAll(orchestra);}}/*outWind3.play MIDDLE_CPercussion.play MIDDLE_CStringed3.play MIDDLE_CBrass.play() MIDDLE_CWoodwind.play()MIDDLE_C*/
8.2.4缺陷—“覆盖”私有方法
public class Private{private void f(){ //缺陷:“覆盖”私有方法System.out.println("我是父类私有的f方法");}public static void main(String[] args) {Private p = new Drive();p.f();}}class Drive extends Private{void f(){System.out.println("我是子类的f方法");}}输出:我是父类私有的f方法
重载:只看方法名和参数列表
重写:返回值,方法名称,参数列表与父类一致
结论:在子类中,对于父类的private方法,名字最好不要一样
8.2.5 缺陷:域与静态方法
只有普通方法的调用可以是多态的
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(); // UpcastSystem.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() = 1sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0*///:~
静态方法不是多态的
8.3 构造器和多态
父类的构造器总是在子类的构造过程中调用的,而且按照继承层次逐渐向上链接,以便使得每个父类的构造器得以调用。因为初始化
8.3.2 继承与清理
销毁的顺序应该与初始化顺序相反,因为万一某个子对象要依赖与其他对象。
import static net.mindview.util.Print.*;class Characteristic {private String s;Characteristic(String s) {this.s = s;print("Creating Characteristic " + s);}protected void dispose() {print("disposing Characteristic " + s);}}class Description {private String s;Description(String s) {this.s = s;print("Creating Description " + s);}protected void dispose() {print("disposing Description " + s);}}class Amphibian {private Characteristic p = new Characteristic("can live in water");private Description t = new Description("Both water and land");Amphibian() {print("Amphibian()");}protected void dispose() {print("Amphibian dispose");t.dispose();p.dispose();}}public class Frog extends Amphibian {private Characteristic p = new Characteristic("Croaks");private Description t = new Description("Eats Bugs");public Frog() { print("Frog()"); }protected void dispose() {print("Frog dispose");t.dispose();p.dispose();super.dispose();}public static void main(String[] args) {Frog frog = new Frog();print("----------------------");frog.dispose();}}输出:Creating Characteristic can live in waterCreating Description Both water and landAmphibian()Creating Characteristic CroaksCreating Description Eats BugsFrog()----------------------Frog disposedisposing Description Eats Bugsdisposing Characteristic CroaksAmphibian disposedisposing Description Both water and landdisposing Characteristic can live in waterProcess finished with exit code 0
8.3.3 构造器内部多态方法的行为
基本构造器调用的时候,导出类的构造器没有调用;但是可以在父类的构造器中调用被子类覆盖的方法
class Glyph {void draw() { print("Glyph.draw()"); }Glyph() {print("Glyph() before draw()");draw();print("Glyph() after draw()");}}class RoundGlyph extends Glyph {private int radius = 1;RoundGlyph(int r) {radius = r;print("RoundGlyph.RoundGlyph(), radius = " + radius);}void draw() {print("RoundGlyph.draw(), radius = " + radius);}}public class PolyConstructors {public static void main(String[] args) {new RoundGlyph(5);}}输出:/*Glyph() before draw()RoundGlyph.draw(), radius = 0Glyph() after draw()RoundGlyph.RoundGlyph(), radius = 5*/
编写构造器准则:避免调用其他方法。在构造器内部唯一能够安全调用的方法是父类中的final方法,因为这些方法不能够被覆盖。
8.4 协变返回类型
子类中被覆盖的方法 可以返回父类方法的返回类型的 某种导出类型
class Grain {public String toString() { return "Grain"; }}class Wheat extends Grain {public String toString() { return "Wheat"; }}class Mill {Grain process() { return new Grain(); }}class WheatMill extends Mill {Wheat process() { return new Wheat(); }}public class CovariantReturn {public static void main(String[] args) {Mill m = new Mill();Grain g = m.process();System.out.println(g);// m = new WheatMill();// g = m.process();Mill m1 = new WheatMill();Grain g1 = m1.process();System.out.println(g1);}}/*GrainWheat*/
