本文利用代码例子解释向上转型与向下转型,文末有举例整合原代码。
首先,我们要知道:转型发生在继承后,也就是父类子类存在的前提下。其次,我们要清楚:向下转型的前提是已经发生了向上转型,向下转型是再次转回来而已。最后,我们要明白:向上转型和向下转型都有三种方法:直接赋值、作为参数和作为返回值类型。另外,我们要注意:向下转型要用到强制类型转换,从而完成接收。
定义三个类:Animals 类、NiuMa 类、A 类:
class Animals{ //Animals类,作为父类public void eat1(){System.out.println("几乎都是肉食动物");}
}class NiuMa extends Animals{ //NiuMa类:作为子类public void eat2(){System.out.println("杂食动物");}public void sleep(){System.out.println("都要睡觉。");}
}class A{ //A类:作为参数和返回类型要用到。public void func1(Animals animals){animals.eat1();System.out.println("父类形参");}public void func3(NiuMa niuMa){niuMa.sleep();System.out.println("子类形参");}public NiuMa func2(){System.out.println("此时返回值类型与public后的引用类型一致");return new NiuMa();} public Animals func4(){System.out.println("此时返回值类型与public后的引用类型不一致!");return new NiuMa();}public Animals func5(){System.out.println("此时返回值类型与public后的引用类型一致");return new Animals();}public NiuMa func6(){System.out.println("此时返回值类型与public后的引用类型不一致!");return (NiuMa)new Animals();}
}
Animals 类:作为父类,具有公共方法 eat1() 。
NiuMa 类:作为子类,具有子类独有方法 sleep() 。
A 类:作为其他类,要借助该类实现参数类型和返回值类型的转型。
向上转型:
一、直接赋值法:
class Text{public static void main(String[] args) {Animals people1 = new NiuMa();people1.eat1();//调用继承后公共部分的方法,没重写调用没重写的,重写了调用重写后的。}
}
父类引用子类对象:Fu dui1 = new Zi();
可用该对象调用继承后公共部分的方法,若该方法被子类重写,则调用重写后的方法。
二、作为参数法:
class Text{public static void main(String[] args) {NiuMa niuma1 = new NiuMa();A a1 = new A();a1.func1(niuma1);}
}
形参为父类,实参为子类。
在方法中实现向上转型,即在 func 方法中已经完成转型任务,完成调用其他方法的实现。
三、作为返回值类型法:
class Text{public static void main(String[] args) {//返回值类型法向上转型://利用func2():public后面为子类,返回值类型为子类A a1 = new A();//用引用类型直接接收:NiuMa niuma1 = a1.func2();//正常接收。Animals animals1 = a1.func2();//发生了向上转型//new对象进行接收:NiuMa niuma2 = new NiuMa();Animals animals2 = new Animals();niuma2 = a1.func2(); //正常接收animals2 = a1.func2();//发生了向上转型//利用finc4():public后面为父类,返回值类型为子类A a2 = new A();//用引用类型直接接收:NiuMa niuma3 = (NiuMa) a2.func4();//相当于发生了一次向下转型Animals animals3 = a2.func4();//正常接收。//new对象进行接收:NiuMa niuma4 = new NiuMa();Animals animals4 = new Animals();niuma4 = (NiuMa) a2.func4(); //相当于又发生了一次向下转型。animals4 = a2.func4();//正常接收//结论:当public后面为父类,返回值为子类时,实际返回值为父类类型。}
}
此块代码主要看利用 func2() 的,因为返回值类型参考的是 public 后为父类还是子类。
父类引用,接收返回类型为子类的方法,实现向上转型。
向下转型:
一、直接赋值法:
class Text{public static void main(String[] args) {Animals people2 = new NiuMa();//要先发生一次向上转型NiuMa people3 = (NiuMa) people2; //将people2强制类型转换成NiuMa类,用people3接收people1.eat1();people3.sleep();}
}
要先发生向上转换:Fu dui2 = new Zi();
向下转换:Zi dui3 = (ZI) dui3;
二、作为参数法:
class Text{public static void main(String[] args) {Animals animals1 = new NiuMa();//先发生强制类型转换A a2 = new A();a2.func3((NiuMa) animals1);}
}
形参为子类,实参为父类。
在方法中实现向下转型,即在 func 方法中已经完成转型任务,完成调用其他方法的实现。
传参时要进行强制类型转换。
三、作为返回值类型法:
public Text{public static void main(String[] args) {//返回值类型法向下转型://利用func5():public后面为父类,返回值类型为父类。A a1 = new A();//用引用类型直接接收:NiuMa niuma1 = (NiuMa) a1.func5();//发生了向下转型Animals animals1 = a1.func5();//正常接收//new对象进行接收:NiuMa niuma2 = new NiuMa();Animals animals2 = new Animals();niuma2 = (NiuMa) a1.func5();//发生了向下转型animals2 = a1.func5();//正常接收//利用func6():public后面为子类,返回值类型为父类。A a2 = new A();//用引用类型直接接收:NiuMa niuma3 = a2.func6();//正常接收Animals animals3 = a2.func6();//相当于向上转型//new对象进行接收:NiuMa niuma4 = new NiuMa();Animals animals4 = new Animals();niuma4 = a2.func6(); //正常接收animals4 = a2.func6();//相当于向上转型//小结:当public后面为子类,返回值类型为父类的时候,返回值类型为子类。}
}
此块代码主要看利用 func5() 的,因为返回值类型参考的是 public 后为父类还是子类。
子类引用,接收返回类型为父类的方法,实现向上转型,要注意进行强制类型转换。
注意点:
一、以下为错误代码:
class Text{public static void main(String[] args) {//⭐错误代码:Animals people = new Animals();A a = new A();a.func3(people);//形参只能向下兼容,不能向上兼容。向下兼容的时候传入子类则会发生向上转型。}
}
此时代码直接会报错:
因为该代码与是否发生向上转型或向下转型无关。而向下转型的本质是已经发生过向上转型,通过强制类型转换从而回来罢了,但是该处错误直接类型不匹配。
由此我们也可以得出一个结论:
形参只能向下兼容,不能向上兼容。向下兼容的时候传入子类则会发生向上转型。
二、关于返回值类型同方法上下不匹配:
在 func4() 与 func6() 中,public 后的引用类型与返回值类型不匹配,但在主函数中用某引用类型去接收该返回值的时候,一定要用 pubic 后的引用类型。
三、为什么会发生向上转型和向下转型?
向上转型是为了调用父类中的方法,向下转型是为了使用完后转回子类,以此才能调用父类中没有的方法。
总结:
向上转型:自动转型,父类引用子类对象。for调父类方法。
向下转型:强制类型转换,本质上就是子类已经发生了向上转型,再次转回来而已。for调父类中没有的子类方法。
以下为整合代码:
放入 Idea 里分别把 main 方法放开运行理解更深刻:
class Animals{public void eat1(){System.out.println("几乎都是肉食动物");}
}
class NiuMa extends Animals{public void eat2(){System.out.println("杂食动物");}public void sleep(){System.out.println("都要睡觉。");}
}
class A{public void func1(Animals animals){animals.eat1();System.out.println("父类形参");}public void func3(NiuMa niuMa){niuMa.sleep();System.out.println("子类形参");}public NiuMa func2(){System.out.println("此时返回值类型与public后的引用类型一致");return new NiuMa();}public Animals func4(){System.out.println("此时返回值类型与public后的引用类型不一致!");return new NiuMa();}public Animals func5(){System.out.println("此时返回值类型与public后的引用类型一致");return new Animals();}public NiuMa func6(){System.out.println("此时返回值类型与public后的引用类型不一致!");return (NiuMa)new Animals();}}
public class ZhuAn {public static void main4(String[] args) {//主要为向下转型。//返回值类型法向下转型://利用func5():public后面为父类,返回值类型为父类。A a1 = new A();//用引用类型直接接收:NiuMa niuma1 = (NiuMa) a1.func5();//发生了向下转型Animals animals1 = a1.func5();//正常接收//new对象进行接收:NiuMa niuma2 = new NiuMa();Animals animals2 = new Animals();niuma2 = (NiuMa) a1.func5();//发生了向下转型animals2 = a1.func5();//正常接收//利用func6():public后面为子类,返回值类型为父类。A a2 = new A();//用引用类型直接接收:NiuMa niuma3 = a2.func6();//正常接收Animals animals3 = a2.func6();//相当于向上转型//new对象进行接收:NiuMa niuma4 = new NiuMa();Animals animals4 = new Animals();niuma4 = a2.func6(); //正常接收animals4 = a2.func6();//相当于向上转型//小结:当public后面为子类,返回值类型为父类的时候,返回值类型为子类。}public static void main3(String[] args) {//主要为向上转型://返回值类型法向上转型://利用func2():public后面为子类,返回值类型为子类A a1 = new A();//用引用类型直接接收:NiuMa niuma1 = a1.func2();//正常接收。Animals animals1 = a1.func2();//发生了向上转型//new对象进行接收:NiuMa niuma2 = new NiuMa();Animals animals2 = new Animals();niuma2 = a1.func2(); //正常接收animals2 = a1.func2();//发生了向上转型//利用finc4():public后面为父类,返回值类型为子类A a2 = new A();//用引用类型直接接收:NiuMa niuma3 = (NiuMa) a2.func4();//相当于发生了一次向下转型Animals animals3 = a2.func4();//正常接收。//new对象进行接收:NiuMa niuma4 = new NiuMa();Animals animals4 = new Animals();niuma4 = (NiuMa) a2.func4(); //相当于又发生了一次向下转型。animals4 = a2.func4();//正常接收//结论:当public后面为父类,返回值为子类时,实际返回值为父类类型。}public static void main2(String[] args) {//传参法:在方法内实现向上转型,在方法内调用方法。//向上转型:NiuMa niuma1 = new NiuMa();A a1 = new A();a1.func1(niuma1);//向下转型:Animals animals1 = new NiuMa();//先发生强制类型转换A a2 = new A();a2.func3((NiuMa) animals1);//⭐错误代码://Animals people = new Animals();//A a = new A();//a.func3(people);//形参只能向下兼容,不能向上兼容。向下兼容的时候传入子类则会发生向上转型。}public static void main1(String[] args) {//直接赋值法//向上转型Animals people1 = new NiuMa();people1.eat1();//调用继承后公共部分的方法,没重写调用没重写的,重写了调用重写后的。//向下转型Animals people2 = new NiuMa();//要先发生一次向上转型NiuMa people3 = (NiuMa) people2; //将people2强制类型转换成NiuMa类,用people3接收people1.eat1();people3.sleep();}
}