首页 > 代码库 > Think in Java(五):多态

Think in Java(五):多态

1. 动态绑定
又称"后期绑定"或"运行时绑定",它的含义就是在运行时判断对象的类型,从而调用恰当的方法
public class Shapes {
	private static RandomShapeGenerator gen = new RandomShapeGenerator();

	public static void main(String[] args) {
		Shape[] shapeArr = new Shape[9]; 
		// 填充数组
		for (int i = 0; i < shapeArr.length; i++){
			shapeArr[i] = gen.next();
		}
		// 动态调用方法
		for (Shape shape : shapeArr){ 
			shape.draw(); 
		}
	}
}

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();
			}
	}
}

class Shape {
	public void draw() {
		System.out.println("Shape.draw()");
	}

	public void erase() {
		System.out.println("Shape.erase()");
	}
}

class Circle extends Shape {
	public void draw() {
		System.out.println("Circle.draw()");
	}

	public void erase() {
		System.out.println("Circle.erase()");
	}
}

class Triangle extends Shape {
	public void draw() {
		System.out.println("Triangle.draw()");
	}

	public void erase() {
		System.out.println("Triangle.erase()");
	}
}

class Square extends Shape {
	public void draw() {
		System.out.println("Square.draw()");
	}

	public void erase() {
		System.out.println("Square.erase()");
	}
}
/* Output:
Triangle.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Circle.draw()
*///:~

2. "覆盖"私有方法:
我们期望的输出是public f(),但是由于private方法被自动认为是final方法,而且对子类是屏蔽的,因此在这种情况下,
Derived类中的f()方法就是一个全新的方法;
结论:只有非private方法才可以被覆盖,所以在子类中,对于父类中的private方法,最好采用不同的名字。

public class PrivateOverride {
	private void f() {
		print("private f()");
	}

	public static void main(String[] args) {
		PrivateOverride po = new Derived();
		po.f();
	}
}

class Derived extends PrivateOverride {
	public void f() {
		print("public f()");
	}
} 
/* Output:
private f()
*///:~

3. 构造器和多态:
复杂对象调用构造器要遵循下面的顺序:
1) 调用父类构造器(如有多层,则会不断递归下去)
2) 按声明顺序调用成员的初始化方法
3) 调用子类构造器的主体
class Meal {
	Meal() {
		print("Meal()");
	}
}

class Bread {
	Bread() {
		print("Bread()");
	}
}

class Cheese {
	Cheese() {
		print("Cheese()");
	}
}

class Lettuce {
	Lettuce() {
		print("Lettuce()");
	}
}

class Lunch extends Meal {
	Lunch() {
		print("Lunch()");
	}
}

class PortableLunch extends Lunch {
	PortableLunch() {
		print("PortableLunch()");
	}
}

public class Sandwich extends PortableLunch {
	private Bread bread = new Bread();

	private Cheese cheese = new Cheese();

	private Lettuce lettuce = new Lettuce();

	public Sandwich() {
		print("Sandwich()");
	}

	public static void main(String[] args) {
		new Sandwich();
	}
} 
/* Output:
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()

4. 编写构造器时,应尽可能简单地使对象进入正常状态,如果可以的话,避免调用其它方法。
下面的例子我们会发现radius的值为0,为什么?
一个动态绑定的方法调用会深入到继承层次结构内部,它可以调用子类的方法,然而此时方法所操纵的成员还没有进行初始化。

class Glyph {
	void draw() {
		print("Glyph.draw()");
	}

	Glyph() {
		print("Glyph() before draw()");
		this.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);
	}
} 
/* Output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*///:~

之所以调用的是子类的draw方法,this的含义是谁调用我,this指向的就是谁,Glyph构造器是通过RoundGlyph类构造器中super()方法触发,所以this指向的是RoundGlyph中的draw方法


5. "向上转型"与"向下转型"
向上转型:List list = new ArrayList(); list.add("xxx") 此时调用的其实是ArrayList的方法
注意:向上转型之后,无法访问子类中的新加的方法

向下转型:其实就是强转,前提是我们知道它是某一种类型,否则运行时会报ClassCastException

Think in Java(五):多态