多态
Java里提供四种类型的多态方法,
- Coercion,就是数据类型显示或隐式转换
- Overloading,函数名称相同,但是参数不同
- Parametric,就是类似C++模板,Java通过generics来实现。
- Subtype,一种类型可以作为另一种类型的subtype起作用。当subtype对象出现在父类的上下文中时,在subtype对象上执行父类的操作,会导致subtype上对应的操作执行。例如,假设Circle是Point的子类,都有draw方法,将一个Circle对象赋值给一个Point对象,然后调用该Point对象的draw方法,会导致真正调用Circle的draw方法。
Upcasting和Late Binding
Circle对象有x,y坐标,可以是Point的子类,再加上radius变量。
Circle c = new Circle(10, 20, 30);
Point p = c;
将Circle对象转换为Point对象时,不需要cast,因为Circle至少是一个Point,这种转换就被称为upcasting(将子类对象赋值给父类对象)。以上方法就实现了subtype多态,但p对象也失去了部分子类特有的feature,这是subtype的代价。subtype除了将子类对象赋值给父类对象外,还需要在子类对象里override
父类的draw方法(在Point这个例子里)。
父类的draw方法(在Point这个例子里)。
这时,调用p的draw方法时,实际上会去执行Circle类里的draw方法。
p对象是怎么知道去调用Circle类里的draw方法呢?在编译的时候,编译器是不知道去调用哪个方法的,它能做的就是检查p里面是否有这个方法并且参数和返回值正确,然后在这个地方插入一条指令,让Java在运行时,去获取该对象的正确引用,并调用正确的draw方法。这个任务被称作late binding。
Late binding用于所有非final的对象方法,对于其他方法,编译器知道调用哪个具体类的方法,这个任务被称作early binding。
Abstract Classes and Abstract Methods
如果要在Circle和Point类的基础上,再实现一个Rectangle类,这个时候不大适合再去extend Point类,因为矩形不适合表示成一个点加上长宽。
因为Circle,Point和Rectangle都是一种图形Shape且都有各自的draw方法。因此可以实现一个Shape class来让另外三个继承:
class Shape
{
void draw()
{
}
}
Shape[] shapes = new Shape[] { new Point(10, 20), new Circle(10, 20, 30),
new Rectangle(20, 30, 15, 25) };
上面Shape对象有个问题,但有用户直接new Shape(),这个是没有意义的,为了预防这种情况,Java提供了抽象类的概念。abstract class Shape
{
abstract void draw(); // semicolon is required
}
abstract关键字表示这个对象不能直接实例化。abstract也可以用于没有实际内容的成员函数。关于抽象类的几个注意事项
- abstract和final不能用于同时声明一个类,因为abstract表示一个类不能直接对象化,而final表示一个类不能被继承,这就是矛盾的。
- 当声明一个成员函数是abstract时,也要声明它所属的类是abstract。一个具体的类不能包含一个抽象的成员函数。
- 当继承一个抽象类时,要么实现它所有的抽象成员函数,要么自己也声明为一个抽象类。
- 一个抽象类可以包含非抽象的成员函数。
Downcasting and Runtime Type Identification
Circle c = (Circle) p;
上面把Point类型转换成Circle类型(父到子)称作downcast,downcast有时候是安全的,例如p本身原来是由Circle类型转换来的,现在只是再转换回去。有些时候是不安全的,例如当p本身最开始就是Point类型,而转换成Circle c后,c再调用Circle里特有的函数时,系统就会扔出异常,此时可以通过instanceof关键字来判断是否可以安全的进行downcast。例如以下例子:public class DowncastArrayDemo
{
public static void main(String[] args)
{
ColoredPoint[] cptArray = new ColoredPoint[1];
cptArray[0] = new ColoredPoint(10, 20, 5);
Point[] ptArray = cptArray;
System.out.println(ptArray[0].getX()); // Output: 10
System.out.println(ptArray[0].getY()); // Output: 20
// System.out.println(ptArray[0].getColor()); // Illegal
if (ptArray instanceof ColoredPoint[])
{
ColoredPoint cp = (ColoredPoint) ptArray[0];
System.out.println(cp.getColor());
}
}
}