JavaRush /Курсы /Java Syntax Pro /Полиморфизм

Полиморфизм

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

1. Киты и коровы

Есть такой интересный зоологический факт: корова гораздо ближе к киту, чем, например, к бегемоту. Оказывается, коровы и киты — близкие родственники.

Так вот. Расскажем вам о еще одном очень мощном инструменте ООП — это полиморфизм. У него есть четыре особенности.


2. Наследование — не панацея

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

А тут приходит заказчик проекта и говорит, что хочет выпустить новый уровень игры, где все действия происходят в море, а главным героем будет кит.

Вы начали проектировать класс Кит и поняли, что он лишь немного отличается от класса Корова. Логика работы обоих классов очень похожа, и вы решили использовать наследование.

Полиморфизм в java

Класс Корова идеально подходит на роль класса-родителя: в нем есть все необходимые переменные и методы. Достаточно только добавить киту возможность плавать. Но есть проблема: у вашего кита есть ноги, рога и колокольчик. Ведь эта функциональность реализована внутри класса Корова. Что тут можно сделать?

Полиморфизм в java. Наследование

3. Переопределение метода

К нам на помощь приходит переопределение (замена) методов. Если мы унаследовали метод, который делает не совсем то, что нужно в нашем новом классе, мы можем заменить этот метод на другой.

Переопределение метода

Как же это делается? В нашем классе-потомке мы объявляем такой же метод, как и метод класса-родителя, который хотим изменить. Пишем в нем новый код. И все: как будто старого метода в классе-родителе и не было.

Вот как это работает:

Код Описание
class Cow
{
   public void printColor ()
   {
      System.out.println("Я — белая");
   }

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

class Whale extends Cow
{
   public void printName()
   {
      System.out.println("Я — кит");
   }
}
  • Тут определены два класса — Cow и Whale
  • Whale унаследован от Cow
  • В классе Whale переопределен метод printName()
public static void main(String[] args)
{
   Cow cow = new Cow();
   cow.printName();
}
Данный код выведет на экран надпись:
Я — корова
public static void main(String[] args)
{    Whale whale = new Whale();
   whale.printName();
}
Данный код выведет на экран:
Я — кит

После наследования класса Cow и переопределения метода printName, класс Whale фактически содержит такие данные и методы:

class Whale
{
   public void printColor()
   {
      System.out.println("Я — белая");
   }

   public void printName()
   {
      System.out.println("Я — кит");
   }
}
Ни о каком старом методе мы и не знаем.

Комментарии (187)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Виктор Уровень 21
18 сентября 2025
Хорошо когда твой стакан пуст и в него можно налить воды, а не душнить в комментах как все́ неправильно.
I'll kick them all Уровень 5
29 июля 2025
Прям пособие по неправильной иерархии классов для чайников. =) Согласно мадам Лисков класс-потомок - конкертный случай класса предка.. И его поведение не должно нарушать поведение базового класса. А следовательно, правильная иерархия будет такая - Animal - базовый класс с функциональностью, общей для коровы и кита. Cow - класс с присущим для коров поведением Whale - класс с присущим для китов поведением. И теперь: мы можем работать с любым из данных классов по интферфейсу класса Animal и они будут вести себя предсказуемо.

// Пример полиморфизма
List<Animal> animals = new ArrayList<>();
animals.add(new Cow());
animals.add(new Whale());
for (var animal: animals) {
    // вот тут происходит работа по полиморфной ссылке
    animal.move(); // корова идет, кит плывет. 
}
И да, в Java-библиотеке принцип подстановки Лисков нарушен много раз, но это не означает, что нужно вводить понятие полиморфизма с заранее неправильных примеров.
Anonymous #3585174 Уровень 33
21 июля 2025
like
Flanky Уровень 22
14 июля 2025
А в чем отличие от @Override?
Cryptosin Уровень 24
14 февраля 2025
Почему в Java нет множественного наследования классов, но есть множественное наследование интерфейсов? Java поддерживает множественное наследование интерфейсов, даже если они содержат default-методы (начиная с Java 8), но требует явного разрешения конфликтов. Ответ с учетом default-методов: 1. Проблема ромбовидного наследования (Diamond Problem) Если бы Java поддерживала множественное наследование классов, то возникала бы ситуация, когда два родительских класса содержат метод с одинаковым названием и реализацией. При наследовании от обоих классов компилятор не смог бы однозначно выбрать, какую реализацию использовать. Пример (если бы Java разрешала множественное наследование классов):

class A {
    void show() { System.out.println("A"); }
}

class B extends A {
    void show() { System.out.println("B"); }
}

class C extends A {
    void show() { System.out.println("C"); }
}

// Ошибка: какой метод show() наследует D?
class D extends B, C { }
Здесь D наследуется от B и C, у которых разные версии метода show(). Java не может решить, какую реализацию выбрать, поэтому множественное наследование классов запрещено. 2. Почему интерфейсы можно наследовать множественно? До Java 8 интерфейсы содержали только сигнатуры методов, а их реализацию предоставлял класс, реализующий интерфейс. Это устраняло проблему ромбовидного наследования, потому что компилятор требовал от разработчика самостоятельно определить поведение методов. Но с Java 8 в интерфейсах появились default-методы, содержащие реализацию:

interface A {
    default void show() { System.out.println("A"); }
}

interface B {
    default void show() { System.out.println("B"); }
}

class C implements A, B {
    @Override
    public void show() {
        // Разрешение конфликта
        A.super.show();
    }
}
Cryptosin Уровень 24
14 февраля 2025
Здесь Java требует явного выбора метода, если несколько интерфейсов предоставляют реализацию одного и того же метода. Если интерфейсы не содержат реализацию (abstract-методы), проблем нет, так как класс обязан их реализовать. 3. Как Java разрешает конфликты при множественном наследовании интерфейсов? 1 - Если два интерфейса содержат метод без реализации (abstract), то класс должен сам его реализовать:

interface A {
    void show();
}

interface B {
    void show();
}

class C implements A, B {
    @Override
    public void show() {
        System.out.println("C's implementation");
    }
}
2 - Если два интерфейса содержат default-методы с одинаковыми названиями, компилятор требует явного указания, какую версию использовать:

interface A {
    default void show() { System.out.println("A"); }
}

interface B {
    default void show() { System.out.println("B"); }
}

class C implements A, B {
    @Override
    public void show() {
        // Выбираем реализацию из интерфейса A
        A.super.show();
    }
}
Просто A.show(); не сработает, потому что show() — это нестатический метод интерфейса, а нестатические методы вызываются через объект.

interface A {
    default void show() { System.out.println("A"); }
}

class C implements A {
    @Override
    public void show() {
        A.show(); // Ошибка компиляции!
    }
}
Компилятор выдаст ошибку:

Non-static method 'show()' cannot be referenced from a static context
Проблема в том, что default-методы принадлежат конкретному экземпляру класса, а не интерфейсу как таковому. Почему нужно A.super.show();? Вызов A.super.show(); означает:
Cryptosin Уровень 24
14 февраля 2025
Использовать реализацию метода из интерфейса A Обращаться к родительскому интерфейсу, а не к текущему классу

interface A {
    default void show() { System.out.println("A"); }
}

interface B {
    default void show() { System.out.println("B"); }
}

class C implements A, B {
    @Override
    public void show() {
        A.super.show(); // Явно выбираем метод из A
    }
}

public class Main {
    public static void main(String[] args) {
        C obj = new C();
        obj.show(); // Выведет "A"
    }
}
Здесь A.super.show(); позволяет явно указать, что мы хотим использовать метод из A, а не B. Вывод - A.show(); не работает, потому что show() — нестатический метод. - A.super.show(); — единственный правильный способ вызвать default-метод из интерфейса внутри класса. - Это правило работает только внутри методов экземпляра (instance methods), потому что super означает обращение к родителю для текущего объекта. 3 - Если один интерфейс содержит default-метод, а другой объявляет такой же метод abstract, то побеждает abstract-метод, и класс обязан его реализовать:

interface A {
    default void show() { System.out.println("A"); }
}

interface B {
    void show(); // Абстрактный метод
}

class C implements A, B {
    @Override
    public void show() {
        System.out.println("C's own implementation");
    }
}
В этом случае default-метод A игнорируется, так как B требует явной реализации. Вывод 1 - Java запрещает множественное наследование классов, потому что это ведёт к проблемам с ромбовидным наследованием и дублированием состояний. 2 - Множественное наследование интерфейсов разрешено, потому что до Java 8 интерфейсы не содержали реализаций. 3 - С появлением default-методов Java требует от разработчика явно разрешать конфликты при реализации нескольких интерфейсов с одинаковыми методами.
Виктор Уровень 21
18 сентября 2025
Очень интересно, не все́ понятно) и не должно.
Nikita Sergeevich Уровень 16
16 января 2025
Как я хотел побыстрее добраться до этой темы. Скипал скучные задачи про списки и даты наконец-то хватило маны до 13 уровня.
Zhenyok Avramenko Уровень 19
10 ноября 2025
ну наконец то всё объяснили на коровках и котятах, не зря скипал
Fat_Tony Уровень 19
28 ноября 2024
Конечно пример вообще не подходящий, что в коде кит наследует от коровы, если мы заново прописываем метод? Гораздо логичнее было бы наследовать от какого-нибудь класса млекопитающее, или что-то типо того.
Ринат Уровень 22
5 декабря 2024
Пойду форточку открою )))
JhonFarell Уровень 30
6 декабря 2024
Ну чувак так-то прав. У нас изначальная корова умеет ходить, и решить проблему просто тем, что мы убираем ключевое свойство класса - это какой-то идиотизм. Оно вообще не отражает суть наследования. Ты любую другую лекцию на тему прочитай, там ключевой пункт - это вопрос "Является ли класс A частностью класса B"? Является ли кит коровой? Очевидно, что нет. А вот с млекопитающим - вполне рабочая схема: что корова, что кит ими являются, а значит могут быть наследниками класса. Тогда у нас будет 3 условных метода eat(), move(), sleep(), которые адекватно переопределяются в классах-наследниках. Так что никакой духоты, просто хреново написанный материал. Меня лично эта лекция в ступор ввела, я 3 раза перечитывал и пытался понять, каким образом они решили проблему "хождения", пока не принял, что просто отбрасывается метод и, типа, все в ажуре.
Ринат Уровень 22
6 декабря 2024
Тебя только эта лекция смутила? У меня тут горит жопа так, что из космоса наверное видно
JhonFarell Уровень 30
6 декабря 2024
Не, к материалу дохрена и больше вопросов, на самом деле. Регулярно в ступор впадаешь от прочитанного, но конкретно здесь мы обсуждаем корову)
Nikita Sergeevich Уровень 16
16 января 2025
А вы как хотели) чтобы все было на блюдце? И чтобы потом когда работать начали чтобы тоже ТЗ заказчик правильное писал, все для вас).
r3diska Уровень 38
14 февраля 2025
Дело в том, что для разработчика не всегда будет явно наследование одного от другого, у вас по работе не будет классов, например, Посуда и наследующийся от нее класс Тарелка, будет много абстрактных вещей, где вы даже не додумаетесь до того, что есть признаки схожести. Попытайтесь материал рассмотреть под разными углами, эти лекции составляли далеко не глупые люди, считаю, что создатели сделали много интересных заковырок, помогающих глубже прочувствовать программирование и работу в целом
Deus Vult Уровень 32
26 ноября 2024
Наконец-то нормальная лекция и интересная тема
Rustam Уровень 3 Student
17 октября 2024
Здорово, что все на таких простых примерах объяснили. Хочется ещё углубиться в тему ООП
RazdolbayZateynik Уровень 28 Expert
20 октября 2024
углубишься, в core будет по 10-15 задач за лекцию и в основном по ооп
Rustam Уровень 3 Student
22 октября 2024
Рад слышать!
rockentrav Уровень 34
23 ноября 2024
и как оно там?
Rustam Уровень 3 Student
29 ноября 2024
Там то же самое)
Илья Зарайский Уровень 26
10 сентября 2024
Урок понравился. Задач в этом блоке не хватило. Хочется побольше. Всем удачи в изучении новой темы ✌