Thinking in Java 系列 ---(六)多态

文章目录

  • Polymorphism
    • 方法的动态绑定和静态绑定
        • 成员变量和static
      • 可维护性
      • 可扩展性
    • Designing with inheritance 继承的设计
    • 总结 summary

Polymorphism

多态:many forms 多种形式
多态又叫做动态绑定,或者运行期间绑定polymorphism (also called dynamic binding or late binding or run-time binding)
多态是处理类型的解耦。But polymorphism deals with decoupling in terms of types.

多态的存在有三个前提:
1.继承
2.子类要重写父类的方法
3.父类引用指向子类 Parent p = new Child();

这时用父类引用调用方法时,会先从子类中寻找该方法

方法的动态绑定和静态绑定

基本类型都是在编译器就已经确认了的。所以绑定是针对方法来说的。

静态绑定:
在程序执行前方法已经被绑定(也就是说在编译过程中就已经知道这个方法到底是哪个类中的方法),此时由编译器或其它连接程序实现。java当中的方法只有final,static,(private)和构造方法是前期绑定,因为final方法不允许重写

动态绑定(多态):
在运行时根据具体对象的类型进行绑定。提供了一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。
举例:子类重写父类方法

//父类
public class Parent {

    protected String name = "ParentName";

    public void method() {
        System.out.println("ParentMethod");
    }
}
//子类
public class Child extends Parent {
    protected String name = "ChildName";

    public void method() {
        System.out.println("ChildMethod");
    }

    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.name);
        p.method();
    }
}
/*
ParentName
ChildMethod*/

1、子类的对象(由父类的引用handle)调用到的是父类的成员变量,运行时(动态)绑定针对的范畴只是对象的方法,而属性还是采取静态绑定。所以多态说的只是方法而不包含成员变量
2、执行p.method()时会先去调用子类重写的method方法执行,若子类没有则向上转型去父类中寻找。
所以在向上转型的情况下,对象的方法可以找到子类,而对象的属性还是父类的属性。

成员变量和static

成员变量和static都没有多态。
子类创建的成员变量为新的成员变量,虽然他可能名字和父类成员变量相同。但是这不会发生,因为实际中我们都把成员变量使用private修饰。
static修饰的方法,为静态绑定只会创建一次,所以static方法存在于哪个类,会被直接调用,而没有多态属性。

可维护性

举例:比如这个例子中,可以使用创建每个子类的重载方法,然后传入子类引用来调用子类,但是这样很繁琐且不易维护,这里就用到多态,将子类引用作为父类的类型传入,这个时候调用的方法首先从子类中寻找,如果子类重写了该方法则调用子类的。

class Instrument {
    public void play(Note n) {
        print("Instrument.play()");
    }
}
class Wind extends Instrument {
    // Redefine interface method:
    public void play(Note n) {
        System.out.println("Wind.play() " + n);
    }
}
class Stringed extends Instrument {
    public void play(Note n) {
        print("Stringed.play() " + n);
    }
}

class Brass extends Instrument {
    public void play(Note n) {
        print("Brass.play() " + n);
    }
}

public class Music2 {
    public static void tune(Instrument i) { i.play(Note.MIDDLE_C); }

    public static void main(String[] args) {

        Wind flute = new Wind();
        Stringed violin = new Stringed();
        Brass frenchHorn = new Brass();
        tune(flute);//调用方法传入的是Instrument父类的引用类型,但是调用的是子类的对象。
        tune(violin);
        tune(frenchHorn);
    }
}/* Output
Wind.play() MIDDLE_C
Stringed.play() MIDDLE_C
Brass.play() MIDDLE_C
*///:~

也就是说我们在使用父类引用调用方法的时候直接传入一个父类引用就可以了,不必创建多个方法来传入每个子类的引用,这就是多态的好处。

可扩展性

Thinking in Java 系列 ---(六)多态_第1张图片
下面代码是实现这个图:

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
*///:~

从这里我们可以看出,我们可以在Instrument[]数组中添加更多的对象,只要他是Instrument的子类,父类都可以调用该方法更加方便扩展。

Designing with inheritance 继承的设计

当选择使用继承和组合时,先尝试使用组合。因为继承可能让设计变得复杂。
A better approach is to choose composition first, especially when it’s not obvious which one you should use.

class Actor {
	public void act() {
	}
}

class Happyactor extends Actor {
	@Override
	public void act() {
		print("HappyActor");
	}
}

class SadActor extends Actor {
	@Override
	public void act() {
		print("SadActor");
	}
}

class Stage {
	private Actor actor = new Happyactor();

	public void change() {
		actor = new SadActor();
	}

	public void performPlay() {
		actor.act();
	}
}

public class Transmogrify {
	public static void main(String[] args) {

		Stage stage = new Stage();
		stage.performPlay();
		stage.change();
		stage.performPlay();
	}
}/* Output
HappyActor
SadActor
*///:~

A general guideline is “Use inheritance to express differences in behavior, and fields to express variations in state.” In the preceding example, both are used; two different classes are inherited to express the difference in the act( ) method, and Stage uses composition to allow its state to be changed. In this case, that change in state happens to produce a change in behavior.所以用继承来展示行为的不同,多态来展示重写方法的多种形态。(这段需要好好理解)

总结 summary

向上转型总是安全的,因为把子类引用转化为父类引用,然后调用方法的范围更小了。
向下转型并不安全,因为范围扩大。
多态代表不同的形式,也就是说有你有父类相同的接口,但是使用这个接口可以表现不同的形式。
Polymorphism means “different forms.” In object-oriented programming, you have the same interface from the base class, and different forms using that interface: the different versions of the dynamically bound methods.

使用多态必须有继承所以多态不是独立的而是结合很多种技术,你需要努力实践来掌握它,当掌握了之后你会更快速而有效的进行开发java程序。

你可能感兴趣的:(Thinking,in,Java,Thinking,in,Java,读书笔记)