向上转型与向下转型
本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 30 课,共 87 课。
在上一课中,你了解了父类型变量如何保存子类对象:Animal myPet = new Dog()。这种赋值称为向上转型,它会自动发生,因为 Dog 始终是 Animal。
向上转型会沿着继承层次向上移动。这是隐式的,而且始终安全:
Dog dog = new Dog();
Animal animal = dog; // 向上转型:自动完成向上转型后,你只能访问父类中定义的方法。子类特有的方法会被隐藏,尽管底层对象仍然是 Dog。
向下转型方向相反,它会将父类引用转换回子类类型。这需要显式转换,因为这样做并不总是安全的:
Animal animal = new Dog(); // 向上转型
Dog dog = (Dog) animal; // 向下转型:需要显式类型转换
dog.bark(); // 现在可以调用 Dog 的方法了向下转型有风险。如果对象实际上不是你要转换成的类型,Java 会在运行时抛出 ClassCastException:
Animal animal = new Cat();
Dog dog = (Dog) animal; // 能编译,但运行时会崩溃!编译器无法捕获这个错误,因为它只能看到声明的类型。只有在程序运行时,才能知道对象的实际类型。在下一课中,你将学习如何在向下转型前安全地检查对象的类型。
挑战
简单我们来构建一个媒体播放器系统,演示向上转型和向下转型的实际效果。你将创建一个媒体类型层级,并练习在父类和子类引用之间进行转换。
你将把代码分成 4 个文件:
Media.java:创建所有媒体类型共有的基类。每个媒体项都有一个title字段(String)。添加一个用于初始化该字段的构造函数、一个 getter 方法getTitle(),以及一个会打印以下内容的方法play():Playing: [title]Song.java:创建一个继承自 Media 的类。歌曲还有一个额外的artist字段(String)。使用super初始化父类。重写play(),使其打印:Playing song: [title] by [artist]。添加一个会打印以下内容的方法showArtist():Artist: [artist]Podcast.java:创建另一个继承自 Media 的类。播客有一个episode字段(int)。重写play(),使其打印:Playing podcast: [title] - Episode [episode]。添加一个会打印以下内容的方法showEpisode():Episode: [episode]Main.java:使用你的媒体类型演示向上转型和向下转型。你会收到 4 个输入:歌曲标题、艺人姓名、播客标题和集数。首先,创建一个 Song,并将其向上转型为 Media 引用。调用这个引用的
play(),观察运行时多态的效果。接下来,将这个 Media 引用向下转型回 Song,并调用
showArtist()来访问子类特有的方法。然后,创建一个 Podcast,并将其向上转型为 Media 引用。调用这个引用的
play()。最后,将这个 Media 引用向下转型回 Podcast,并调用
showEpisode()。
你会收到 4 个输入:歌曲标题(String)、艺人姓名(String)、播客标题(String)和集数(int)。
你的输出应包含 4 行:歌曲占 2 行(播放输出和艺人信息),播客占 2 行(播放输出和集数信息)。注意,向上转型让你可以统一处理不同的媒体类型,而向下转型则让你可以访问它们各自的独特功能!
亲手试试
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取输入
String songTitle = scanner.nextLine();
String artistName = scanner.nextLine();
String podcastTitle = scanner.nextLine();
int episodeNumber = scanner.nextInt();
// TODO: 创建一个 Song 对象,并将其向上转型为 Media 引用
// TODO: 通过 Media 引用调用 play() 方法(演示多态)
// TODO: 将 Media 引用向下转型回 Song
// TODO: 通过 Song 引用调用 showArtist() 方法
// TODO: 创建一个 Podcast 对象,并将其向上转型为 Media 引用
// TODO: 通过 Media 引用调用 play() 方法
// TODO: 将 Media 引用向下转型回 Podcast
// TODO: 通过 Podcast 引用调用 showEpisode() 方法
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Java 在线编译器