一句话概述多态:父类引用指向子类实现。 多态的作用是消除类型之间的耦合关系:它可以将从同一父类中导出的子类视为同一类型来处理。这样我们使用某些代码时只要考虑父类就可以。
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{
@Override
public 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);
}
}
/*out
Wind3.play MIDDLE_C
Percussion.play MIDDLE_C
Stringed3.play MIDDLE_C
Brass.play() MIDDLE_C
Woodwind.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(); // 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
*///:~
静态方法不是多态的
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 water
Creating Description Both water and land
Amphibian()
Creating Characteristic Croaks
Creating Description Eats Bugs
Frog()
----------------------
Frog dispose
disposing Description Eats Bugs
disposing Characteristic Croaks
Amphibian dispose
disposing Description Both water and land
disposing Characteristic can live in water
Process 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 = 0
Glyph() 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);
}
}
/*
Grain
Wheat
*/