Menu
CoddyTech

向上转型与向下转型

本课属于 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;  // 能编译,但运行时会崩溃!

编译器无法捕获这个错误,因为它只能看到声明的类型。只有在程序运行时,才能知道对象的实际类型。在下一课中,你将学习如何在向下转型前安全地检查对象的类型。

challenge icon

挑战

简单

我们来构建一个媒体播放器系统,演示向上转型和向下转型的实际效果。你将创建一个媒体类型层级,并练习在父类和子类引用之间进行转换。

你将把代码分成 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() 方法
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Java 在线编译器