网上很多关于此类的文章,我觉得讲的不是太清楚,以己之见帮助大家。
本文主要列出几种多态的形态,避免踩坑,话不多说,进入正题:
先给出父类和子类:
//父类
class Animal{
public void show() {
System.out.println("this is a Animal");
}
}
//子类Dog
class Dog extends Animal {
@Override
public void show() {
System.out.println("this is a Dog");
}
public void sleep() {
System.out.println("dog is sleep");
}
}`
接下来是普通的向上转型:
public class TypeConversion {
public static void main(String[] args) {
//实例化Animal类,并新建一个Dog类的引用变量引用该实例,
Animal an=new Dog();//向上转型
an.show();
//调用的是子类重写的show方法,System.out.println("this is a Dog");
//an.sleep();报错
为什么会报错?
就是用父类的引用变量去引用子类的实例,这是允许的。当向上转型之后,父类引用变量可以访问子类中属于父类的属性和方法,但是不能访问子类独有的属性和方法。例子中由于子类重写了父类的show()方法,所以调用的show()方法是子类的show()方法,输出结果为:“this is a apple”,而调用子类的test()方法则会报错。
接下来向下转型:
Dog dog=(Dog)an;//向下转型
dog.sleep();// System.out.println("dog is sleep");
此时调用子类的方法,才不会报错,并不是所有的对象都可以向下转型,只有当这个对象原本就是子类对象通过向上转型得到的时候才能够成功转型。
接下来踩坑环节,直接new出子类,好像是强行转为父类:
Dog dog1=new Dog();
Animal an1=(Dog)dog;
an1.show();//调用的依旧是子类重写的show方法,System.out.println("this is a Dog");
我称他伪向下转型,先new子类,然后强转,其实等于Animal an1=new Dog();依旧是普通的向上转型。
小技巧:编译看左边,运行看右边,只不过需要你两段代码一起看。
继续脑洞,直接new父类,然后强转为子类:
Animal an2=new Animal();
Dog dog2=(Dog) an2;
dog2.sleep();
dog2.show();
结果可想而知,依旧报错,Exception in thread “main” java.lang.ClassCastException,因为真理在这:子类引用不能指向父类对象,并不是所有的对象都可以向下转型,只有当这个对象原本就是子类对象通过向上转型得到的时候才能够成功转型。
接下来继续加一个儿子继承父类:
//子类Cat
class Cat extends Animal{
@Override
public void show() {
System.out.println("this is a Cat");
}
public void sleep() {
System.out.println("Cat is sleep");
}
}
这下我烦了,直接这样玩,用现有的Dog的子类向上转型的对象an强转,
Cat cat1=(Cat) an; //java.lang.ClassCastException
上述代码虽然能够编译成功,但是在运行的时候会报错,因为an对象是由dog对象向上转型得到的,只能够向下转型成dog对象,不能够向下转型成cat对象。
其实可以从基本类型的强转看判断,被强转的类型永远比目标类型大,其间必须有直接的关系:
int i = (int) 4L;
byte b= (byte) (90+40);
总结
直接new父类,然后强转为子类,虽然编译不报错,运行会报错。
无论后面有多少儿子继续继承,用第一次向上得来的对象继续强转,虽然编译不报错,运行会报错永远都是ClassCastException。
子类引用不能指向父类对象,并不是所有的对象都可以向下转型,只有当这个对象原本就是子类对象通过向上转型得到的时候才能够成功转型。