什么是向上转型和向下转型
在Java继承体系中,认为基类(父类/超类)在上层,导出类(子类/继承类/派生类)在下层,因此向上转型的意思就是把子类对象转成父类类型,即将父类的引用指向子类对象;向下转型的意思就是把父类对象转成子类类型,即将子类的引用指向父类对象。
向上转型
先看一下向上转型的例子:
首先定义一个动物类,即Animal类,并赋予一个eat()方法
class Animal
{
public void eat()
{
System.out.println("父类的 eating...");
}
}
然后定义一个鸟类,即Bird类,继承Animal类并重写Animal类的eat()方法,并赋予一个方法fly()方法
class Bird extends Animal
{
@Override
public void eat()
{
System.out.println("子类重写的父类的 eatting...");
}
public void fly()
{
System.out.println("子类新方法 flying...");
}
}
最后定义一个Sys类,实现向上转型,先看代码
public class Sys
{
public static void main(String[] args)
{
Animal b=new Bird();
b.eat();
sleep(new Male());
sleep(new Female());
}
public static void sleep(Human h) //方法的参数是父类!!
{
h.sleep();
}
}
好,看完代码了,我们来解释一下向上转型是如何进行的吧!
Animal b=new Bird(); //向上转型
b.eat(); // 调用的是子类的eat()方法
b.fly(); // 报错!!!!!-------b虽指向子类对象,但此时子类作为向上转型的代价丢失和父类不同的fly()方法(子类向上转型会丢失与父类不同(父类所不拥有的方法)
sleep(new Male());
sleep(new Female());
public static void sleep(Human h) //方法的参数是父类
{
h.sleep();
}
如上代码就是用的向上转型,若是不用向上转型,那么有多少个子类就得在这儿写多少种不同的睡觉方法,如果有一千个,甚至更多子类,还不写的奔溃,所以使用向上转型既提高了代码的可扩展性,又减少了程序员的工作,何乐而不为呢?
再来看一下向下转型:
向下转型
和上面一样,我们先定义一个水果类,即Fruit类,并赋予一个myName()方法
class Fruit{
public void myName(){
System.out.printf("superclass:Fruit");
}
}
接着我们定义一个苹果类,即Apple类,继承Fruit类并重写Fruit类的myName()方法,并赋予一个myMore()方法
class Apple extends Fruit{
public void myName(){
System.out.printf("sonclass:Apple");
}
public void myMore(){
System.out.printf("A little Apple of Fruit");
}
}
最后定义一个Sys类来实现向下转型,但实现向下转型前需要先实现向上转型,所以这也是为什么我们前面要介绍向上转型的原因,先看代码
public class Sys{
public static void main(String[] args) {
Fruit a=new Apple();
a.myName();
Apple aa=(Apple)a;
aa.myName();
aa.myMore();;
Fruit f=new Fruit();
Apple aaa=(Apple)f;
aaa.myName();
aaa.myMore();
}
}
这里有必要再说一下向上转型:
向上转型可分为两种,一种是隐式转换,一种是强制转换。
class A {
}
interface C {
}
public class B extends A implements C {
public static void main(String[] args) throws Exception {
/**
* 向上转型
*/
A a1 = new B();
A a2 = new B();
C c = (C) a2;
Class<> clazz = Class.forName("p.B");
A a3 = (A) clazz.newInstance();
}
}
当子类有两种或多种父类类型,并且这些父类类型并不在同一个继承体系内时,如果要将子类对象的引用从一个父类传给另一个父类,就需要进行强制转换。
向下转型是你向上转型后才能执行的操作,一定要牢记这一点。
类型还原
public class B {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Object o = new B();
B b = (B) o;
b = (B) Class.forName("p.B").newInstance();
}
}
向下转型的实例
首先,我们定义一个空接口Food,不赋予任何方法
public interface Food{
}
然后新建一个Vegetables,并实现Food接口:
public class Vegetables implements Food{
public void green(){
System.out.println("Vegetables is green food");
}
public void nutrition(){
System.out.println("Vegetables is very nourishing");
}
}
新建一个Meal,并实现Food接口:
public class Meal implements Food{
public void testgood(){
System.out.println("Meal test good");
}
public void nutrition(){
System.out.println("Meal is also nourishing");
}
}
新建一个Fruit类,并实现Food接口:
public class Fruit implements Food{
public void necessary(){
System.out.println("Fruit is necessary food for man");
}
public void nutrition(){
System.out.println("Meal is also nourishing");
}
}
上面的程序都很简单,定义了一个Food接口,并在Vegetables、Meal、Fruit类分别实现它,每一个方法也很简单,仅仅打印了一行信息而已。
接下来,我们想象一个情景:我们在商城买食品,食品有很多,但基本上可以分为蔬菜、肉类和水果,等等,这些都属于食品。电子产品是抽象的。好,那么我们决定买一台Thinkpad,一个鼠标和一个键盘。
这时,我们需要一个购物车来放我们需要买的食品。我们可以添加进购物车,然后通过购物车还能知道存放的食品数量,能拿到对应的食品。
那么,一个购物车类就出来了,如下:
import java.util.ArrayList;
import java.util.List;
public class ShopCar{
private List<Food> mlist = new ArrayList<Food>();
public void add(Food food){
mlist.add(food);
}
public int getSize(){
return mlist.size();
}
public Electronics getListItem(int position){
return mlist.get(position);
}
}
List 集合是用来存放食品的,add 方法用来添加食品到购物车,getSize 方法用来获取存放的食品数量,getListItem 方法用来获取相应的食品。
大家可能会疑惑的是为什么是放 Electronics 的泛型,而不是放Vegetables,Meal,Fruit等?
想象一下,如果是List,肯定放不进肉Meal吧,难道我们要生成3个集合?这里是定义了3个食品类,但是我如果有100种食品呢,难道要定义100个集合?
这太可怕了。所以之前,我们写了一个Food接口,提供了一个Food的标准,然后让每一个Food子类都去实现这个接口。
实际上这里又涉及到了向上转型的知识点,我们虽然在add 方法将子类实例传了进来存放,但子类实例在传进去的过程中也进行了向上转型。
所以,此时购物车里存放的子类实例对象,由于向上转型成Food,已经丢失了子类独有的方法,以上述例子来分析,Vegetables实例就是丢失了green() 和nutrition() 这两个方法,而Meal实例就是丢失了testgood()和nutrition()这两个方法。
但是实际上,这些食物丢失任何一个方法都是我们所不希望的。
接着我们写一个测试类 Test 去测试购物车里的食品。
public class Test{
public static final int Vegetables= 0;
public static final int Meal= 1;
public static final int Fruit = 2;
public static void main(String[] args){
ShopCar shopcar = new ShopCar();
shopcar.add(new Vegetables());
shopcar.add(new Meal());
shopcar.add(new Fruit ());
System.out.println("购物车存放的食品数量为 ——> "+shopcar.getSize());
Vegetables vegetables= (Vegetables)shopcar.getListItem(Vegetables);
vegetables.green();
vegetables.nutrition();
System.out.println("-------------------");
Meal meal = (Meal)shopcar.getListItem(Meal);
meal .testgood();
meal .nutrition();
System.out.println("-------------------");
Fruit fruit = (Fruit )shopcar.getListItem(Fruit);
fruit .necessary();
fruit .nutrition();
}
}
举个例子分析一下
//开始测试Vegetables蔬菜
Vegetables vegetables= (Vegetables)shopcar.getListItem(Vegetables)
vegetables.green()
vegetables.nutrition()
(Vegetables)shopcar.getListItem(Vegetables)
这句代码是获取到Food类型的实例。不是Vegetables的实例
通过向下转型,赋值给子类引用
Vegetables vegetables= (Vegetables)shopcar.getListItem(Vegetables);
样子类实例又重新获得了因为向上转型而丢失的方法(gren和nutrition)
向下转型的意义
向下转型最大的作用是Java的泛型编程,作用巨大,Java中集合框架作用大都如此。
而在Android开发中,我们在Layout文件夹,用xml写的控件。为什么能在Activity等组件中通过 findViewById() 方法找到呢?为什么 findViewById(R.id.textview) 方法传入TextView的id后,还要转型为TextView呢?这就是 Java 向下转型的一个应用。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)