JavaRush /Курсы /Java Syntax Pro /Полиморфизм, часть 3

Полиморфизм, часть 3

Java Syntax Pro
17 уровень , 3 лекция
Открыта

1. Приведение типов

Переменные ссылочных типов (классов) тоже можно преобразовывать к разным типам. Однако это работает только в рамках одной иерархии типов. Давайте рассмотрим на простом примере. Допустим, у нас есть такая иерархия классов: классы ниже наследуются от классов выше.

Приведение типов

Приведение ссылочных типов, как и примитивных тоже делится на расширяющие и сужающее.

Мы видим, что класс Кот унаследован от класса ДомашнееЖивотное, а класс ДомашнееЖивотное в свою очередь от класса Животное.

Если мы напишем такой код:

Животное котик = new Кот();

Это расширяющее приведение типа: его еще называют неявным. Мы расширили ссылку котик, и теперь она ссылается на объект типа Кот. При таком приведении мы не сможем через ссылку котик вызвать методы, которые есть у класса Кот, но которых нет у класса Животное.

Сужающее приведение (или явное) происходит в обратную сторону:

Кот котэ = (Кот) котик;

Мы явно указали, что хотим привести ссылку, которая хранится в переменной котик (типа Животное) к типу Кот.



2. Проверка типа объекта

Но тут нужно быть очень осторожным. Если вы сделаете так:

Животное зверь = new Кот();
Волк серыйВолк = (Волк) зверь;

Компилятор пропустит этот код, а вот во время выполнения программы возникнет ошибка! JVM кинет вам исключение:

Exception in thread "main" java.lang.ClassCastException: Кот cannot be cast to Волк

Ссылку на объект Кот можно сохранить только в переменные, которые имеют тип класса-родителя для класса Кот: ДомашнееЖивотное, Животное и Object.

Почему же так?

Все дело в том, что ссылка на объект используется для того, чтобы обращаться к методам и переменным этого объекта. И если мы сохраним в переменную типа Животное ссылку на объект Кот, то никаких проблем с этим не возникнет: у типа Кот всегда будет переменная и методы типа Животное: он же их унаследовал!

А вот если бы JVM разрешила сохранить ссылку на объект Кот в переменную типа Волк, могла бы возникнуть ситуация, когда у переменной серыйВолк вызывается метод, который отсутствует у объекта Кот, на который эта переменная и ссылается. Поэтому такое сохранение не разрешается.

В Java есть специальный оператор — instanceof, который позволяет проверить, можно ли сохранить объект определенного типа в переменную определенного типа. Выглядит он достаточно просто:

переменная instanceof Тип

Пример:

Животное зверь = new Кот();
if (зверь instanceof Волк)
{
   Волк серыйВолк = (Волк) зверь;
}

Такой код не вызовет ошибок даже во время выполнения.

Вот еще несколько примеров с описанием ситуации:

Расширение типа Описание
Cow cow = new Whale();

Классическое расширение типа — оператор преобразования типа не требуется. Теперь у объекта типа Whale можно вызывать только методы, описанные в классе Cow.

Компилятор разрешит вызвать у переменной cow только те методы, которые есть у ее типа — класса Cow.

Сужение типа
Cow cow = new Whale();
if (cow instanceof Whale) {
   Whale whale = (Whale) cow;
}
Классическое сужение типа: нужно добавить проверку типа и оператор приведения типа.
Переменная cow типа Cow хранит ссылку на объект класса Whale.
Мы проверяем, что это так и есть, и затем выполняем операцию преобразования (сужение) типа. Или как ее еще называют —
type cast
.

Cow cow = new Cow();
Whale whale = (Whale) cow; // exception
Ссылочное сужение типа можно провести и без проверки типа объекта.
При этом, если в переменной cow хранился объект не класса Whale, будет сгенерировано исключение — InvalidClassCastException.


3. Вызов оригинального метода: super

Иногда бывает нужно не заменить метод родительского класса на свой при переопределении метода, а лишь немного дополнить его.

Было бы классно, если бы мы могли в нашем методе вызвать такой же метод родительского класса, а потом еще выполнить какой-то свой код. Ну или сначала выполнить свой код, а потом вызвать метод родительского класса.

И такая возможность в Java есть. Вызов метода именно родительского класса делает так:

super.метод(параметры);

Примеры:

class МирноеВремя
{
   public double getPi()
   {
      return 3.14;
   }
}

class ВоенноеВремя extends МирноеВремя
{
   public double getPi()
   {
      return super.getPi()*2;  // 3.14*2
   }
}

В военное время значение Pi может достигать четырех, а в нашем случае вообще 6! Это, конечно, шутка, но она демонстрирует, как это все может работать.

Вот еще пара примеров, чтобы немного прояснить ситуацию:

Код Описание
class Cow
{
   public void printAll()
   {
      printColor();
      printName();
   }

   public void printColor()
   {
      System.out.println("Я — белый");
   }

   public void printName()
   {
      System.out.println("Я — корова");
   }
}

class Whale extends Cow
{
   public void printName()
   {
      System.out.print("Это неправда: ");
      super.printName();
      System.out.println("Я — кит");
   }
}
Классы Cow и Whale
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printAll();
}
На экран будет выведена надпись:
Я — белый
Это неправда: Я — корова
Я — кит

Да, это непростой материал: честно говоря, он один из самых сложных в ООП. Однако знать и понимать его необходимо.


Комментарии (265)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Nervo Id Уровень 18
8 ноября 2025
Сколько же воды и как же темы в целом перемешаны... С каждым уроком всё больше и больше нет желания продолжать учиться ТУТ. Дак ещё и моментами одну и ту же тему по 3-4 раза повторяют. Ооооооооооооооооооооооооооооооооооооочень много воды.
Exaltyr777 Уровень 25
2 августа 2025
А почему Кот котэ = (Кот) котик; здесь приведение типа идёт именно с переменной а не с объектом? До этого всё писали про работу с объектами, а тут бац - и уже переменная - главный герой ) Я конечно понимаю что возможно просто надо смириться что так решили разрабы Oracle. Но просто реально непривычно и немного запутан конкретно этот момент.
Anonymous #3585174 Уровень 33
21 июля 2025
like
4el0vek3 Уровень 36
21 июля 2025
like??? Именно это ты хотел написать да, Anonymous #3585174? Под каждой лекцией я видел твой "like" — и вот наконец догнал тебя! Спасибо, что ты есть! Ты стал для меня мотивацией: я хотел обогнать тебя, поэтому уделял JavaRush много времени, и теперь это вошло в привычку. Заниматься уже не в тягость, как было в самом начале. like?
Anonymous #3585174 Уровень 33
21 июля 2025
good luck 😎
I'll kick them all Уровень 5
29 июля 2025
I'm right behind you
Anonymous #3612532 Уровень 1
31 июля 2025
like?
kxtgkzq _ Уровень 23
18 октября 2025
like!
Sasha Volov Уровень 23
9 мая 2025
Не уверен, что ситуацию вида:

Животное котик = new Кот();
корректно называть расширяющим приведением. Сразу возникает ассоциация с расширяющим и сужающим преобразованиями для примитивных типов, которые в документации указаны как Widening Conversion и Narrowing Conversions. Для ссылочных же типов употребляются такие термины как Upcasting и Downcasting, которые обычно переводят как восходящее и нисходящее приведения. P.S. Хотя, стоит упомянуть, что даже на англоязычных ресурсах с данными терминами возникает путаница. Очень часто встречал термины "Widening casting" и "Narrowing casting". Поэтому... Вопрос о том, являются ли понятия приведение и преобразование синонимичными, остается открытым.
Emillionaire Уровень 23
22 апреля 2025
Как легко и понятно после потоков и дат пошло...
Anonymous #3564975 Уровень 28
23 марта 2025
InvalidClassCastException - это из какой версии java? Вообще, в уроках куча опечаток и неточностей. Кажется давно пора завести багтрекер
18 февраля 2025
"Да, это непростой материал: честно говоря, он один из самых сложных в ООП. Однако знать и понимать его необходимо." Да вроде не такой уж и сложный, все понятно по логике и хорошим примерам
Yasin Akhmadov Уровень 22
15 февраля 2025
Не знаю, что на счёт прошлых уровней, однако ООП вы преподаёте классно)
Андрей Уровень 20
10 декабря 2024
Для решения задач попробуйте сочетание Ctrl + O в вашей IDE, что вызовет переопределение метода с вызовом super
Cryptosin Уровень 24
13 февраля 2025
Советовал бы меньше нажимать таких кнопочек, пока не доведете ручной ввод до автоматизма
18 февраля 2025
пользоваться подсказками идеи вполне можно, например при решении последней задачи он сразу выдаёт часть (super.printinfo). а так же он выдал @Override, который я не знал, а оказалось что это важно и полезно. И после разбора того, что он выдал уже можно приступать к продолжению решения