有时候需要把父类对象指向子类对象,有时候需要把父类对象转换成子类对象。例如我们可以说某一只狗是一只动物,还可以说这只动物是一条狗,但前提是我们要知道狗肯定是动物,而那只动物必须是一只狗。下面介绍如何把父类指向子类对象,如何把子类对喜爱那个强制转换成子类对象。
1、父类引用指向子类对象
正常情况下,我们会定义某个类的对象引用,然后使用new实例化一个对象,然后把这个引用指向该实例。例如:
Dog dog = new Dog();
假设Dog是Animal的子类,也可以写下面的代码:
Animal animal = new Dog();
Animal animal定义了一个Animal对象引用,new Dog()实例化了一只狗,然后animal指向了这只狗。 这是允许的,就像我们指着一只狗,然后说它是动物,它确实是动物。
还可以写成下面的形式:
Animal animal = dog;
意思是把animal引用指向dog引用指向的实例。
这就是向上转型,不仅仅是父类可以指向子类的对象,只要是祖先类都可以指向子类的实例。典型情况,Object是所有类的祖先类,所以Object类型的对象引用可以指向任何对象。例如下面的代码:
Object o = new Dog();
把父类引用指向子类对象的好处是什么呢?自己可以思考一下,后面的多态性应用中详细介绍。
另外,类似的用法还有:
某个方法的定义如下:
public void setValue(Object o){
this.value = o;
}
这个方法的参数是Object类型,返回值类型是Object。因为参数类型是Object,所以在调用这个方法的时候我们可以给它传递任何类型的参数,包括上面的Dog对象,只要是它的子孙类就可以了。
再看下面的方法:
public Object getValue(){
return value;
}
value可以是任何类型,Dog实例,Animal实例,Date实例,Person实例都可以。
想想数据结构中的链表、队列等等,他们的元素可以是各种类型,就可以使用这种方式,可以设置整数链表、Dog链表,获取它的元素的时候,返回值的类型由元素类型决定,所以参数和返回值类型都应该设置为Object。
总结一句话:任何需要父类对象地方都可以给子类对象。
2、强制类型转换
我们可以把父类引用指向子类对象,但是这样带来的其他问题。假设Dog有一个特殊的行为方法f,如果把Dog实例赋值给了Animal引用,例如下面的代码:
Animal animal = new Dog();
如何访问Dog的f方法呢?
直接写animal.f()肯定不行,编译不能通过,因为animal没有方法f()。
所以这时候还需要把animal再转换成Dog才可以访问方法f。能这样写吗?
Dog d = animal;
不能,变异的时候就会报错类型不匹配。可以使用下面的代码:
Dog d = (Dog)animal;
强制把animal转换成了Dog,也就是把动物转换成了狗。这就是强制类型转换,把父类对象转换成子类对象(子类对象引用指向了父类对象)。
能转换吗?因为我们知道这只动物确实是狗,所以可以转换。
再看下面的代码:
Animal animal = new Cat();
Dog d = (Dog)animal;
单从第2行看,与之前的代码没有区别。但是大家一看就知道有问题,因为代码想把猫转换成狗,这时候会报错的:ClassCastException
这种错误不是语法错误,所以能够编译通过,但是在运行的时候出错。
所以在进行强类型转换的时候,我们要确保能够转换,只有当父类指向的实例是某个子类的对象的时候才可以转换成该类的对象。
如果不能确定可以通过instanceof操作符进行判断。
if(animal instanceof Dog)
Dog d = (Dog)animal;
上一次: 第三十九讲 多态
下一次:第四十一讲 this和super