语句 m(new Student());将对象new Student()赋值给一个Object类型的参数。这条语句等价于
Object o = new Student();
m(o);
由于Student的实例自动地就是Object的实例,所以,语句Object o = new Student()是合法的,它称为隐式转换。
假设想使用下面的语句把对象引用o赋值给Student类型的变量:
Student b = o;
在这种情况下,将会发生编译错误。为什么语句Object o = new Student()可以运行,而语句Student b = o不行呢?原因是Student对象总是Object的实例,但是,Object对象不一定是Student的实例。即使可以看到o实际上是一个Student的对象,但是编译器还没有聪明到懂得这一点。为了告诉编译器o就是一个Student对象,就要使用显式转换。它的语法与基本类型转换的语法很类似,用圆括弧把目标对象的类型括住,然后放到要转换的对象前面,如下所示:
Student b = (Student)o;
总是可以将一个子类的实例转换为一个父类的变量,因为子类的实例永远是它的父类的实例。当把一个父类的实例转换为它的子类变量时,必须使用转换记号“(子类名)”进行显式转换,向编译器表明你的意图。为使转换成功,必须确保要转换的对象是子类的一个实例。如果父类对象不是子类的一个实例,就会出现一个运行异常ClassCastException。例如:如果一个对象不是Student的实例,它就不能转换成Student类型的变量。因此,一个好的经验是,在尝试转换之前,确保该对象是另一个对象的实例。这是可以利用运算符instanceof来实现的。考虑下面的代码:
Object myObject = new Circle();
...
if(myObject instanceof Circle) {
System.out.println("The circle diameter is " + ((Circle)myObject).getDiameter());
...
}
你可能会奇怪为什么必须进行类型转换。变量myObject被声明为Object。声明类型决定了在编译时匹配哪个方法。使用myObject.getDiameter()会引起一个编译错误,因为Object类没有getDiameter方法。编译器无法找到和myObject.getDiameter()匹配的方法。所以,有必要将myObject转换成Circle类型,来告诉编译器myObject也是Circle的一个实例。
为什么没有在一开始就把myObject定义为Circle类型呢?为了能够进行通用程序设计,一个好的经验是把变量定义为父类型,这样,它就可以接收任何子类型的值。
提示: 为了更好地理解类型转换,可以认为它们类似于水果、苹果、橘子之间的关系,其中水果类Fruit是苹果类Apple和橘子类Orange的父类。苹果是水果,所以,问题可以将Apple的实例安全地赋值给Fruit变量。但是,水果不一定是苹果,所以,必须进行显式转换才能将Fruit的实例赋值给Apple的变量。
下列程序演示了多态和类型转换。程序创建两个对象(第5~6行),一个圆和一个矩形,然后调用displayObject方法显示它们(第9~10行)。如果对象是一个圆,displayObject方法显示它的面积和周长(第15行),而如果对象是一个长形,这个方法显示它的面积(第21行)。
public class CastingDemo {
public static void main(String[] args) {
Object object1 = new Circle4(1);
Object object2 = new Rectangle1(1,1);
displayObject(object1);
displayObject(object2);
}
public static void displayObject(Object object) {
if(object instanceof Circle4) {
System.out.println("The circle area is " + ((Circle4)object).getArea());
System.out.println("The circle diameter is " + ((Circle4)object).getDiameter());
}
else if (object instanceof Rectangle1) {
System.out.println("The rectangle area is " + ((Retangle1)object).getArea());
}
}
}
输出:
The circle diameter is 2.0
The rectangle area is 1.0
displayObject(Object object)方法是一个通用程序设计的例子。它可以通过传入Object的任何实例被调用。
程序使用隐式转换将一个Circle对象赋值给object1并且将一个Rectangle对象赋值给Object2(第3~4行),然后调用displayObject方法显示这些对象的信息(第5~6行)。
在displayObject方法中(第8~16行),如果对象是Circle的一个实例,则用显式转换将这个对象转换为Circle对象。使用getArea和getDiameter方法显示这个圆的面积和直径。
只有源对象是目标类的实例时才能进行类型转换。在执行转换前,程序使用instanceof运算符来确保源对象是否是目标类的实例(第9行)。
由于getArea和getDiameter方法在Object类中是不可用的,所以,有必要显式地转换成Circle类型(第10、11行)和Rectangle类型(第14行)。