在了解向上转型和向下转型我们需要先了解什么是编译类型,什么是运行类型。
以如下代码为例,Aniaml是他的编译类型(因为在运行前就确定了),Dog是他的运行类型(因为他有个new的过程要让代码跑起来)。
Animal animal = new Dog();
向上转型就是将子类对象赋值给父类引用。如一下代码,animal是对象引用,而new Cat()才是真正的对象。向上转型无法调用子类特有的属性和方法。
往下的animal.eat()和animal.say()涉及到动态绑定,由此我们可以引出什么是动态绑定。在 Java 中,动态绑定又称运行时绑定,是指程序在运行期间才确定要调用的方法具体实现的机制。与之相对的是静态绑定,这里就不细说了。
如前面所说我们知道animal实际引用的“Cat对象(即运行类型是Cat,可以通过调用animal.getClass()来输出相关信息)。因此在调用eat方法时他会去找Cat类里面eat方法,如果Cat类中没有该方法则向上去父类中找eat方法,若找到Object类依旧没有则报错。
只有方法才有动态绑定,属性遵循的是就近原则。子类重写的say方法中输出的name应该是子类的name(此时最近的就是子类中的name属性),这里可以体现出属性遵循的是就近原则。如果想要访问父类的name可以使用super.name
public class Main {
public static void main(String[] args){
Animal animal = new Cat("小白","小黄","加菲猫"); //向上转型
animal.eat();
animal.say();
//System.out.println(animal.breed); 错误,向上转型不能访问子类的特有方法和属性
}
}
class Animal{
String name;
public Animal(String name) {
this.name = name;
}
void eat(){
System.out.println("调用了Animal类中的eat方法");
}
void say(){
System.out.println("调用了Animal类中的say方法,猫的名字是" + name);
}
}
class Cat extends Animal{
String name; //名字
String breed; //品种
public Cat(String animalName, String catName, String breed) {
super(animalName); //初始化父类name
this.name = catName; //初始化子类name
this.breed = breed;
}
@Override
void eat(){
System.out.println("猫吃鱼");
}
@Override
void say(){
System.out.println("猫的名字是" + name);
}
}
向下转型就是将父类类型的引用强制转换为子类类型的引用。目的是通过父类引用获取子类特有的属性或方法。向下转型依旧是方法调用遵循动态绑定,属性不参与动态绑定,遵循就近原则。
如以下代码中 Cat cat = (Cat)animal是将对象引用(animal)的类型转换成子类类型(即向下转型),此时可以调用子类的特有方法和属性。向下转型后依旧可以使用父类的属性和方法。
向下转型的父类引用必须指向目标子类的对象。比如Animal animal = new Cat("小白","小黄","加菲猫")的运行类型是Cat我们不能将他向下转型成Dog类型,因为我们new出来的对象是Cat类型的。
public class Main {
public static void main(String[] args){
Animal animal = new Cat("小白","小黄","加菲猫"); //向上转型
animal.eat();
animal.say();
Cat cat = (Cat)animal; //向下转型
System.out.println(cat.breed);
}
}
class Animal{
String name;
public Animal(String name) {
this.name = name;
}
void eat(){
System.out.println("调用了Animal类中的eat方法");
}
void say(){
System.out.println("调用了Animal类中的say方法,猫的名字是" + name);
}
}
class Cat extends Animal{
String name; //名字
String breed; //品种
public Cat(String animalName, String catName, String breed) {
super(animalName); //初始化父类name
this.name = catName; //初始化子类name
this.breed = breed;
}
@Override
void eat(){
System.out.println("猫吃鱼");
}
@Override
void say(){
System.out.println("猫的名字是" + name);
}
}
因此我们向下转型时最好使用 instanceof来检查类型,避免抛出 ClassCastException
(类型转换异常)。
void fun(Animal animal){
if(animal instanceof Cat){
Cat cat = (Cat)animal;
System.out.println(cat.breed);
}
}
instanceof
检查类型注:文章中可能存在表述不准确、细节的完整性不足等问题。如果大家发现有错误或需要补充的地方,欢迎随时指出,非常感谢!