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

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

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

1. Но и это еще не все

Предположим, в классе Cow есть метод printAll(), который вызывает два других метода. Тогда код будет работать так:

Код Описание
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.println("Я — кит");
   }
}
public static void main(String[] args)
{
   Whale whale = new Whale ();
   whale.printAll();
}
На экран будет выведена надпись:
Я — белая
Я — кит

Обратите внимание: когда вызывается метод printAll(), написанный в классе Cow, у объекта типа Whale, используется метод printName класса Whale, а не Cow.

Главное — не в каком классе написан метод, а какой тип (класс) объекта, у которого этот метод вызван.

Наследовать и переопределять можно только нестатические методы. Статические методы не наследуются и, следовательно, не переопределяются.

Вот как выглядит класс Whale после применения наследования и переопределения методов:

class Whale
{
   public void printAll()
   {
      printColor();
      printName();
   }

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

   public void printName()
   {
      System.out.println("Я - кит");
   }
}
Вот как выглядит класс Whale после применения наследования и переопределения метода. Ни о каком старом методе printName мы и не знаем.

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

Тут есть еще более интересный момент. Т.к. класс при наследовании получает все методы и данные класса-родителя, объект этого класса разрешается сохранять (присваивать) в переменные класса-родителя (и родителя родителя, и т.д., вплоть до Object). Пример:

Код Описание
public static void main(String[] args)
{    Whale whale = new Whale();
   whale.printColor();
}
На экран будет выведена надпись:
Я — белая
public static void main(String[] args)
{
   Cow cow = new Whale();
   cow.printColor();
}
На экран будет выведена надпись:
Я — белая
public static void main(String[] args)
{
   Object o = new Whale();
   System.out.println(o.toString());
}
На экран будет выведена надпись:
Whale@da435a.

Метод toString() унаследован от класса Object

Это очень ценное свойство: немного позже вы поймете, как это использовать на практике.


3. Вызов метода объекта

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

Вот как это выглядит:

Код Описание
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printName();
}
На экран будет выведена надпись:
Я — кит
public static void main(String[] args)
{
   Cow cow = new Whale();
   cow.printName();
}
На экран будет выведена надпись:
Я — кит

Обратите внимание, что на то, какой именно метод printName() вызовется, от класса Cow или Whale, влияет не тип переменной, а тип объекта, на который она ссылается.

В переменной типа Cow сохранена ссылка на объект типа Whale, и будет вызван метод printName(), описанный в классе Whale.

Это не очень очевидно. Запомните главное правило:

Набор методов, которые можно вызвать у переменной, определяется типом переменной. А какой именно метод/какая реализация вызовется, определяется типом/классом объекта, ссылку на который хранит переменная.

Вы будете постоянно сталкиваться с этим, так что чем раньше запомните, тем лучше.

Комментарии (134)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
4el0vek3 Уровень 36
21 июля 2025
Наследовать и переопределять можно только нестатические методы. Статические методы не переопределяются. Статические методы наследуются, но их нельзя переопределять через @Override. Если в подклассе объявлен статический метод с такой же сигнатурой, как и в родительском классе, происходит сокрытие метода. Это означает, что класс-наследник будет вызывать свою собственную реализацию статического метода, а не родительскую.
Anonymous #3585174 Уровень 33
21 июля 2025
like
ShadyLine Уровень 30
4 апреля 2025
Пока все понятно, хотя все это пока что поверхностно... что дальше...
Seva Уровень 51 Expert
26 января 2025
"Наследовать и переопределять можно только нестатические методы. Статические методы не наследуются и, следовательно, не переопределяются." - неправда. Статические методы наследуются, но не могут быть переопределены. Если вы объявляете один и тот же статический метод в подклассе, вы скрываете метод суперкласса, а не переопределяете его.
Максим Антонов Уровень 27
11 января 2025
ни одной задачи, это не к добру...
MarioCubic Уровень 14
3 февраля 2025
😂
White_Eagle Уровень 20
4 января 2025
Почему в разделе 1 в примере "class Whale" без "extends Cow"? О каком наследовании тогда может идти речь?
NightCrow Уровень 27
10 октября 2024
Да уж... написаное красным поймет только тот, кто знал о чем идет речь и без лектора. Ну или погуглил. Лучше привести хорошую иллюстрацию из интернета. package ООПначало; public class A { void callme(){ System.out.println(" callme() из класса А"); } } class B extends A { void callme() { System.out.println(" callme() из класса B"); } } class C extends A { void callme() { System.out.println(" callme() из класса C"); } } class Dispatch { public static void main(String args[]) { A a = new A(); B b = new B(); C c = new C(); A r; r = a; r.callme(); r = b; r.callme(); r = c; r.callme(); } } ------------------------------------------------------------- callme() из класса А callme() из класса B callme() из класса C Process finished with exit code 0 Так наверное понятнее уже. Переменная r у нас типа A, но отрабатывает callme из того объекта, ссылку на который мы поместили в r (A, B или С). А не был бы метод переопределен, взяли бы его выше в родительском дереве. Дополнительно наверное стоит отметить, что мы могли бы написать вообще Object r, т.к. A наследует от Object. Вот только метод calme тогда вызвать бы не удалось. Потому что Object ничего не знает о методах в A, B и С.
Yasin Akhmadov Уровень 22
15 февраля 2025
О да, вы правы на 100%. Рад, что мои мысли подтвердились.
Ромушка Роман Уровень 32
6 октября 2024
Описано очень хорошо:) Мне не хватило только табличек Excel, где показано, как это устроено в памяти
Vlad Уровень 30
27 июля 2024
Я увидел жопку бага 😮
Kaz Уровень 32
23 июля 2024
Ух ты.. чувствую большое поле для ошибок связанных с этой особенностью наследования будет в реальном программировании. Я помню до этого что-то такое встречалось к конструкции List<String> list = new ArrayList<String>. Возникали ошибки, по вызову методов ArrayList, потому что они отсутствовали у класса List.
Wren Уровень 29
18 августа 2024
Класс-родитель не знает о каких-либо методах класса-наследника, которые в нем содержатся, это да.