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

Полиморфизм

Открыта

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("Я — кит");
   }
}
Ни о каком старом методе мы и не знаем.

Комментарии (190)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Euraks Backend Developer
4 октября, 18:10
🦔
Grigoryvvv
Уровень 28
Expert
16 июля, 22:18
17.07.2026 / 18 уровень
Семен
Уровень 28
6 мая, 12:23
06.05.202026 - 18 lvl
Виктор
Уровень 28
18 сентября 2025, 13:10
Хорошо когда твой стакан пуст и в него можно налить воды, а не душнить в комментах как все́ неправильно.
I'll kick them allSenior Task Kicker в JavaTrush
29 июля 2025, 07:32
Прям пособие по неправильной иерархии классов для чайников. =) Согласно мадам Лисков класс-потомок - конкертный случай класса предка.. И его поведение не должно нарушать поведение базового класса. А следовательно, правильная иерархия будет такая - 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, 10:10
like
Flanky
Уровень 22
14 июля 2025, 09:37
А в чем отличие от @Override?
Cryptosin Crypto Magnat
14 февраля 2025, 10:27
Почему в 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 Crypto Magnat
14 февраля 2025, 10:27
Здесь 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 Crypto Magnat
14 февраля 2025, 10:27
Использовать реализацию метода из интерфейса 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 требует от разработчика явно разрешать конфликты при реализации нескольких интерфейсов с одинаковыми методами.
Виктор
Уровень 28
18 сентября 2025, 13:04
Очень интересно, не все́ понятно) и не должно.
Nikita Sergeevich
Уровень 16
16 января 2025, 20:46
Как я хотел побыстрее добраться до этой темы. Скипал скучные задачи про списки и даты наконец-то хватило маны до 13 уровня.
Zhenyok Avramenko
Уровень 30
10 ноября 2025, 14:34
ну наконец то всё объяснили на коровках и котятах, не зря скипал
Fat_Tony
Уровень 19
28 ноября 2024, 15:13
Конечно пример вообще не подходящий, что в коде кит наследует от коровы, если мы заново прописываем метод? Гораздо логичнее было бы наследовать от какого-нибудь класса млекопитающее, или что-то типо того.
Ринат
Уровень 22
5 декабря 2024, 15:58
Пойду форточку открою )))
JhonFarell
Уровень 30
6 декабря 2024, 13:08
Ну чувак так-то прав. У нас изначальная корова умеет ходить, и решить проблему просто тем, что мы убираем ключевое свойство класса - это какой-то идиотизм. Оно вообще не отражает суть наследования. Ты любую другую лекцию на тему прочитай, там ключевой пункт - это вопрос "Является ли класс A частностью класса B"? Является ли кит коровой? Очевидно, что нет. А вот с млекопитающим - вполне рабочая схема: что корова, что кит ими являются, а значит могут быть наследниками класса. Тогда у нас будет 3 условных метода eat(), move(), sleep(), которые адекватно переопределяются в классах-наследниках. Так что никакой духоты, просто хреново написанный материал. Меня лично эта лекция в ступор ввела, я 3 раза перечитывал и пытался понять, каким образом они решили проблему "хождения", пока не принял, что просто отбрасывается метод и, типа, все в ажуре.
Ринат
Уровень 22
6 декабря 2024, 13:45
Тебя только эта лекция смутила? У меня тут горит жопа так, что из космоса наверное видно
JhonFarell
Уровень 30
6 декабря 2024, 14:14
Не, к материалу дохрена и больше вопросов, на самом деле. Регулярно в ступор впадаешь от прочитанного, но конкретно здесь мы обсуждаем корову)
Nikita Sergeevich
Уровень 16
16 января 2025, 20:44
А вы как хотели) чтобы все было на блюдце? И чтобы потом когда работать начали чтобы тоже ТЗ заказчик правильное писал, все для вас).
r3diska
Уровень 38
14 февраля 2025, 13:03
Дело в том, что для разработчика не всегда будет явно наследование одного от другого, у вас по работе не будет классов, например, Посуда и наследующийся от нее класс Тарелка, будет много абстрактных вещей, где вы даже не додумаетесь до того, что есть признаки схожести. Попытайтесь материал рассмотреть под разными углами, эти лекции составляли далеко не глупые люди, считаю, что создатели сделали много интересных заковырок, помогающих глубже прочувствовать программирование и работу в целом