向上转型与向下转型

向上转型与向下转型是 Java 中关于父子对象转换的两个概念。

假如我们有如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 父类
public class Animals {
public int age = 0;

public void eat(){
System.out.println("Eat...");
}
}

// 子类 继承 父类 Animals
public class Dog extends Animals {
public int age = 10;

@Override
public void eat(){
System.out.println("eat bone...");
}

public void bark(){
System.out.println("dog bark...");
}
}

public class Cat extends Animals{
public int age = 2;
@Override
public void eat(){
System.out.println("eat fish...");
}

public void sleep(){
System.out.println("cat sleep...");
}
}
  • 向上转型:Animals a = new Cat()
  • 向下转型:Cat cat = (Cat) a
  • 注意:只有向上转型的变量才能向下转型。此时实例 a 就拥有子类自己定义的特有方法 cat.sleep()

为什么要向上转型

假设我们有一个宠物店类,其中有一个统计宠物数量的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class AnimalsShop {
// 注意这里用的是Animals泛型,加入宠物时,不必关心宠物的具体类型
private List<Animals> list = new ArrayList<Animals>();
// 当加入一个宠物时,相当于进行了向上转型
public void add(Animals a){
list.add(a);
}

public int getSize(){
return list.size();
}
// 取出的宠物也是经过向上转型的,此时类型为Animals
public Animals getAnimal(int position){
return list.get(position);
}
}

因此可以发现,使用向上转型的方式能够提高代码的灵活性和可扩展性。比如在上述代码中,我们不必关心宠物的具体类型,便可以直接传入 AnimalsShop 类中进行数量统计。

为什么要向下转型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// 电子产品接口
public interface Electronics{

}

// ThinkPad电脑 实现了电子产品接口
public class Thinkpad implements Electronics{

//Thinkpad引导方法
public void boot(){
System.out.println("welcome,I am Thinkpad");
}

//使用Thinkpad编程
public void program(){
System.out.println("using Thinkpad program");
}

}

// 鼠标 实现了电子产品接口
public class Mouse implements Electronics{

//鼠标移动
public void move(){
System.out.println("move the mouse");
}

//鼠标点击
public void onClick(){
System.out.println("a click of the mouse");
}

}

// 购物车 用来装电子产品
public class ShopCar{

private List<Electronics> mlist = new ArrayList<Electronics>();

public void add(Electronics electronics){
mlist.add(electronics);
}

public int getSize(){
return mlist.size();
}

public Electronics getListItem(int position){
return mlist.get(position);
}
}

// 测试类
public class Test{

public static final int THINKPAD = 0;
public static final int MOUSE = 1;

public static void main(String[] args){

//添加进购物车
ShopCar shopcar = new ShopCar();
shopcar.add(new Thinkpad());
shopcar.add(new Mouse());
shopcar.add(new Keyboard());

//获取大小
System.out.println("购物车存放的电子产品数量为 ——> "+shopcar.getSize());

//开始测试thinkpad电脑
Thinkpad thinkpad = (Thinkpad)shopcar.getListItem(THINKPAD);
thinkpad.boot();
thinkpad.program();

System.out.println("-------------------");

//开始测试Mouse鼠标
Mouse mouse = (Mouse)shopcar.getListItem(MOUSE);
mouse.move();
mouse.onClick();

System.out.println("-------------------");
}

}

很多时候,我们需要把很多种类的实例对象,全部扔到一个集合。比如把Thinkpad笔记本,Mouse鼠标等实例对象,全部扔到一个 Shopcar 购物车集合,但是不可能给每个种类都用一个独立的集合去存放。这个时候我们应该寻找到一个标准来统一这些实例,接口就是一个标准。上面提到的笔记本电脑、鼠标都是电子产品,我们将其抽象为一个 Electronic 接口。

同时,我们把很多种类的实例对象,全部扔到存放其父类对象的集合。经过了这个过程,子类实例已经赋值给了父类引用(即完成了向上转型)但在这个过程中,子类丢失了其特有的方法,如果我们想要重新找回这些丢失的方法,就要用到向下转型,就像下面这段代码。

1
2
3
4
//开始测试thinkpad电脑
Thinkpad thinkpad = (Thinkpad)shopcar.getListItem(THINKPAD);
thinkpad.boot();
thinkpad.program();

参考文章:


向上转型与向下转型
http://example.com/2025/03/12/向上转型与向下转型/
作者
Moonike
发布于
2025年3月12日
许可协议