Upcasting и Downcasting
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 30 из 87.
В предыдущем уроке вы увидели, как переменная типа parent может хранить дочерний объект: Animal myPet = new Dog(). Это присваивание называется upcasting и происходит автоматически, потому что Dog всегда является Animal.
Восходящее приведение перемещается вверх по иерархии наследования. Оно выполняется неявно и всегда безопасно:
Dog dog = new Dog();
Animal animal = dog; // Апкастинг - автоматическийПри восходящем приведении можно получить доступ только к методам, определённым в классе parent. Методы, специфичные для дочернего класса, становятся скрытыми, хотя объект под капотом всё ещё является Dog.
Downcasting работает в обратном направлении, преобразуя ссылку parent обратно в дочерний тип. Для этого требуется явное преобразование cast, поскольку это не всегда безопасно:
Animal animal = new Dog(); // Восходящее приведение
Dog dog = (Dog) animal; // Нисходящее приведение — требуется явное приведение
dog.bark(); // Теперь мы можем обращаться к методам DogDowncasting — рискованная операция. Если объект на самом деле не является типом, к которому вы выполняете приведение, Java выдаёт ClassCastException во время выполнения:
Animal animal = new Cat();
Dog dog = (Dog) animal; // Компилируется, но падает во время выполнения!Компилятор не может обнаружить эту ошибку, потому что он видит только объявленные типы. Фактический тип объекта становится известен только во время выполнения программы. В следующем уроке ты узнаешь, как безопасно проверить тип объекта перед Downcasting.
Задание
ЛегкоДавайте создадим систему медиаплеера, демонстрирующую на практике восходящее и нисходящее приведение типов. Вы создадите иерархию типов медиа и попрактикуетесь в преобразовании ссылок между parent и дочерними классами.
Вы распределите свой код по четырём файлам:
Media.java: создайте базовый класс, общий для всех типов медиа. Каждый медиаобъект имеет полеtitle(String). Добавьте конструктор для его инициализации, gettergetTitle()и методplay(), который выводит:Playing: [title]Song.java: создайте класс, который extends Media. У песен есть дополнительное полеartist(String). Используйтеsuperдля инициализации parent. Переопределитеplay(), чтобы он выводил:Playing song: [title] by [artist]. Добавьте методshowArtist(), который выводит:Artist: [artist]Podcast.java: создайте ещё один класс, который extends Media. У подкастов есть полеepisode(int). Переопределитеplay(), чтобы он выводил:Playing podcast: [title] - Episode [episode]. Добавьте методshowEpisode(), который выводит:Episode: [episode]Main.java: продемонстрируйте восходящее и нисходящее приведение типов для ваших типов медиа. Вы получите четыре входных значения: название песни, имя исполнителя, название подкаста и номер эпизода.Сначала создайте Song и выполните восходящее приведение к ссылке Media. Вызовите
play()для этой ссылки, чтобы увидеть полиморфизм времени выполнения в действии.Затем выполните нисходящее приведение этой ссылки Media обратно к Song и вызовите
showArtist(), чтобы получить доступ к методу, специфичному для дочернего класса.После этого создайте Podcast и выполните восходящее приведение к ссылке Media. Вызовите
play()для этой ссылки.Наконец, выполните нисходящее приведение этой ссылки Media обратно к Podcast и вызовите
showEpisode().
Вы получите четыре входных значения: название песни (String), имя исполнителя (String), название подкаста (String) и номер эпизода (int).
Ваша программа должна вывести четыре строки — две для песни (результат вызова play и информация об исполнителе) и две для подкаста (результат вызова play и информация об эпизоде). Обратите внимание, как восходящее приведение позволяет работать с разными типами медиа единообразно, а нисходящее приведение — получать доступ к их уникальным возможностям!
Попробуйте сами
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 и выполнить upcast к ссылке Media
// TODO: Вызвать play() на ссылке Media (демонстрирует полиморфизм)
// TODO: Выполнить downcast ссылки Media обратно к Song
// TODO: Вызвать showArtist() на ссылке Song
// TODO: Создать Podcast и выполнить upcast к ссылке Media
// TODO: Вызвать play() на ссылке Media
// TODO: Выполнить downcast ссылки Media обратно к Podcast
// TODO: Вызвать showEpisode() на ссылке Podcast
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»Потренируйтесь самостоятельно: Онлайн-компилятор Java