将子类对象当做父类类型使用实现了多态的目标,这在Java中叫做向上转型,如下:
Animal an1 = new Cat(); //创建Cat对象,通过Animal父类类型的变量an1引用子类类型的对象
Animal an2 = new Dog(); //创建Dog对象,通过Animal父类类型的变量an2引用子类类型的对象
将子类对象当做父类使用时不需要任何显示地声明,但是,此时不能通过父类变量去调用子类中的特有方法。只能使用父类和子类所共有的方法,其实也就是父类有的方法。
如下为反面示例:
//定义接口Animal
interface Animal {
void shout(); //定义抽象shout方法
}
//定义Cat类实现Animal接口
class Cat implements Animal {
//实现shout方法
public void shout(){
System.out.println("喵喵...");
}
//定义sleep方法
public void sleep(){
System.out.println("猫在睡觉...");
}
}
//定义向上转型测试类
public class UpcastingWrong {
public static void main(String[] args){
Cat cat = new Cat(); //创建Cat类的实例对象
animalShout(cat); //调用animalShout()方法,将cat作为参数传入
}
//定义静态的animalShout()方法,接收一个Animal类型的参数
public static void animalShout(Animal animal){
animal.shout(); //调用实际参数的shout()方法
animal.sleep(); //调用实际参数的sleep()方法
}
}
在上述代码的main方法中,调用animalShout()方法时,传入了Cat类型的对象,而方法的参数类型为Animal类型,这时将Cat对象当做父类Animal类型使用,也就是实现了向上转型。
当编译器检查到代码的第25行时,发现Animal类中没有定义sleep方法,sleep方法只存在于Cat类中,而此时传入的对象已经向上转型当做Animal类型使用,所以找不到sleep方法。
此时会出现报错提示信息:The method sleep() is undefined for the type Animal Java(67108964)
但是此时由于传入的对象是Cat类型,Cat类型中定义了sleep()方法,所以我们可以通过类型转换来调用sleep()方法。
因此可以在animalShout()方法中将Animal类型的变量强制转换为Cat类型。将animalShout()方法代码修改如下:
//定义静态的animalShout()方法,接收一个Animal类型的参数
public static void animalShout(Animal animal){
Cat cat = (Cat) animal; //将animal对象强制转换为Cat类型并由Cat类型的cat引用
cat.shout(); //调用实际参数的shout()方法
cat.sleep(); //调用实际参数的sleep()方法
}
修改后再次编译,即可正常运行。
如上这种将父类当做子类使用的情况,在Java中叫做向下转型,但是这种类型转换也有可能出现错误,比如当传入的对象和向下转型的对象不匹配时。
如下为反面示例:
//定义接口Animal
interface Animal {
void shout(); //定义抽象shout方法
}
//定义Cat类实现Animal接口
class Cat implements Animal {
//实现shout方法
public void shout(){
System.out.println("喵喵...");
}
//定义sleep方法
public void sleep(){
System.out.println("猫在睡觉...");
}
}
//定义Dog类实现Animal接口
class Dog implements Animal {
//实现shout方法
public void shout(){
System.out.println("汪汪...");
}
}
//定义向上转型测试类
public class UpcastingWrong_2 {
public static void main(String[] args){
Dog dog = new Dog(); //创建Dog类的实例对象
animalShout(dog); //调用animalShout()方法,将dog作为参数传入
}
//定义静态的animalShout()方法,接收一个Animal类型的参数
public static void animalShout(Animal animal){
Cat cat = (Cat) animal; //将animal对象强制转换为Cat类型并由Cat类型的cat引用
cat.shout(); //调用实际参数的shout()方法
cat.sleep(); //调用实际参数的sleep()方法
}
}
代码在运行时报错:Exception in thread "main" java.lang.ClassCastException: class Dog cannot be cast to class Cat (Dog and Cat are in unnamed module of loader 'app'
提示Dog类型不能转换为Cat类型,原因是,在调用animalShout()方法时,传入一个Dog对象,在强制类型转换向下转型时,Animal类型的变量无法强制转换为Cat类型。
为了避免这种错误,Java提供了一个关键字 instanceof 它可以判断一个对象是否为某个类(或者接口)的实例或者子类实例,语法格式如下:
对象(或者对象引用变量) instanceof 类(或者接口)
接下来修改上面代码中的animalShout()方法如下:
//定义静态的animalShout()方法,接收一个Animal类型的参数
public static void animalShout(Animal animal){
if (animal instanceof Cat){ //判断animal是否为Cat类的实例对象,如果是,则进行向下转换并调用两个方法
Cat cat = (Cat) animal; //将animal对象强制转换为Cat类型并由Cat类型的cat引用
cat.shout(); //调用实际参数的shout()方法
cat.sleep(); //调用实际参数的sleep()方法
}else{
System.out.println("This animal is not a Cat");
}
}
此时运行结果:This animal is not a Cat
说明instanceof判断animal不是Cat类的实例对象,运行else其后的内容。