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();
}
На экран будет выведена надпись:
Я — белый
Это неправда: Я — корова
Я — кит

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


Комментарии (272)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Grigoryvvv Уровень 19 Expert
18 июля 2026
18.07.2026 / 18 уровень
Денис Уровень 22
8 мая 2026
Толком не объяснили почему расширяющее приведение дает меньше методов к вызову, однако считается расширяющим.
SUNSHINE Уровень 30
3 марта 2026
Не, ну тут даже ГПТ сломался и выдал мне такое. " Расширение типа происходит, когда вы используете более конкретные типы (в данном случае Human и Cat), которые наследуются от более общего типа (Astronauts). Это позволяет вам хранить объекты разных подклассов в одном списке, который имеет общий суперкласс. " "Расширение типа (или "upcasting") происходит, когда вы приводите объект к более общему типу. Например, если у вас есть объект класса Human, который является подклассом Astronauts, то вы можете использовать его как объект типа Astronauts без явного приведения."
Rei Уровень 32
10 февраля 2026
А кто-то смотрит/читает картинки вообще? Стало любопытно, потому что сразу их пролистываю и мотаю к тексту)
Kismet Уровень 23
17 февраля 2026
это миллениальская тема, я вообще их с начала курса скипаю
Nervo Id Уровень 32
8 ноября 2025
Сколько же воды и как же темы в целом перемешаны... С каждым уроком всё больше и больше нет желания продолжать учиться ТУТ. Дак ещё и моментами одну и ту же тему по 3-4 раза повторяют. Ооооооооооооооооооооооооооооооооооооочень много воды.
Данил Рушков Уровень 21
3 января 2026
Да, это непростой материал: честно говоря, он один из самых сложных в ООП. Однако знать и понимать его необходимо.
Exaltyr777 Уровень 25
2 августа 2025
А почему Кот котэ = (Кот) котик; здесь приведение типа идёт именно с переменной а не с объектом? До этого всё писали про работу с объектами, а тут бац - и уже переменная - главный герой ) Я конечно понимаю что возможно просто надо смириться что так решили разрабы Oracle. Но просто реально непривычно и немного запутан конкретно этот момент.
Anonymous #3585174 Уровень 33
21 июля 2025
like
4el0vek3 Уровень 40
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 _ Уровень 1
18 октября 2025
like!
C0N5P1RACY Уровень 1
3 января 2026
mm.. like...
Sasha Volov Уровень 23
9 мая 2025
Не уверен, что ситуацию вида:

Животное котик = new Кот();
корректно называть расширяющим приведением. Сразу возникает ассоциация с расширяющим и сужающим преобразованиями для примитивных типов, которые в документации указаны как Widening Conversion и Narrowing Conversions. Для ссылочных же типов употребляются такие термины как Upcasting и Downcasting, которые обычно переводят как восходящее и нисходящее приведения. P.S. Хотя, стоит упомянуть, что даже на англоязычных ресурсах с данными терминами возникает путаница. Очень часто встречал термины "Widening casting" и "Narrowing casting". Поэтому... Вопрос о том, являются ли понятия приведение и преобразование синонимичными, остается открытым.
Emillionaire Уровень 23
22 апреля 2025
Как легко и понятно после потоков и дат пошло...