Menu
Coddy logo textTech

Upcasting и Downcasting

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 30 из 87.

В предыдущем уроке вы узнали, как переменная родительского типа может содержать объект дочернего класса: Animal myPet = new Dog(). Это присваивание называется восходящим преобразованием (upcasting), и оно происходит автоматически, так как Dog всегда является Animal.

Восходящее преобразование (Upcasting) выполняется вверх по иерархии наследования. Оно является неявным и всегда безопасно:

Dog dog = new Dog();
Animal animal = dog;  // Upcasting - автоматическое

При восходящем преобразовании (upcasting) вы можете получить доступ только к методам, определенным в родительском классе. Методы, специфичные для дочернего класса, становятся скрытыми, даже если объект по-прежнему является Dog внутри.

Понижающее приведение типов (Downcasting) происходит в обратном направлении, преобразуя ссылку родительского класса обратно к дочернему типу. Это требует явного приведения, поскольку это не всегда безопасно:

Animal animal = new Dog();  // Восходящее преобразование (Upcasting)
Dog dog = (Dog) animal;     // Нисходящее преобразование (Downcasting) - требуется явное приведение типов
dog.bark();                 // Теперь мы можем получить доступ к методам Dog

Понижающее приведение типов (downcasting) рискованно. Если объект на самом деле не является тем типом, к которому вы его приводите, Java выбрасывает исключение ClassCastException во время выполнения:

Animal animal = new Cat();
Dog dog = (Dog) animal;  // Компилируется, но падает во время выполнения!

Компилятор не может обнаружить эту ошибку, так как он видит только объявленные типы. Фактический тип объекта становится известен только во время выполнения программы. В следующем уроке вы узнаете, как безопасно проверять тип объекта перед понижающим приведением типов.

challenge icon

Задание

Легко

Давайте создадим систему медиаплеера, которая демонстрирует восходящее (upcasting) и нисходящее (downcasting) преобразование типов в действии. Вы создадите иерархию типов медиа и попрактикуетесь в преобразовании ссылок между родительскими и дочерними классами.

Вы организуете свой код в четырех файлах:

  • Media.java: Создайте базовый класс, который является общим для всех типов медиа. Каждый медиа-элемент имеет поле title (String). Включите конструктор для его инициализации, метод-геттер 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: Продемонстрируйте восходящее и нисходящее преобразование с вашими типами медиа. Вы получите четыре входных значения: название песни, имя исполнителя, название подкаста и номер эпизода.

    Сначала создайте объект Song и выполните восходящее преобразование (upcast) к ссылке типа Media. Вызовите play() по этой ссылке, чтобы увидеть полиморфизм времени выполнения в действии.

    Затем выполните нисходящее преобразование (downcast) этой ссылки Media обратно к Song и вызовите showArtist() для доступа к методу, специфичному для дочернего класса.

    Затем создайте Podcast и выполните восходящее преобразование к ссылке Media. Вызовите play() по этой ссылке.

    Наконец, выполните нисходящее преобразование этой ссылки Media обратно к Podcast и вызовите showEpisode().

Вы получите четыре входных значения: название песни (String), имя исполнителя (String), название подкаста (String) и номер эпизода (int).

Ваш вывод должен содержать четыре строки — две для песни (результат воспроизведения и информация об исполнителе) и две для подкаста (результат воспроизведения и информация об эпизоде). Обратите внимание, как восходящее преобразование позволяет вам единообразно обрабатывать различные типы медиа, в то время как нисходящее преобразование позволяет получить доступ к их уникальным функциям!

Попробуйте сами

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: Вызовите play() для ссылки Media (демонстрирует полиморфизм)
        
        // TODO: Выполните нисходящее приведение ссылки Media обратно к Song
        
        // TODO: Вызовите showArtist() для ссылки Song
        
        // TODO: Создайте Podcast и выполните восходящее приведение к ссылке Media
        
        // TODO: Вызовите play() для ссылки Media
        
        // TODO: Выполните нисходящее приведение ссылки Media обратно к Podcast
        
        // TODO: Вызовите showEpisode() для ссылки Podcast
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование