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).

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

Шпаргалка

В Java восходящее преобразование (upcasting) перемещает вверх по иерархии наследования и происходит автоматически. Объект дочернего класса может быть присвоен переменной родительского типа:

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

При восходящем преобразовании вы можете обращаться только к методам, определенным в родительском классе, даже если объект по-прежнему относится к дочернему типу.

Нисходящее преобразование (downcasting) преобразует ссылку родительского типа обратно в дочерний тип. Оно требует явного приведения типов:

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

Нисходящее преобразование рискованно. Если объект на самом деле не является тем типом, к которому вы его приводите, Java выбросит исключение ClassCastException во время выполнения:

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

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

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

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Проверьте себя

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

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