JavaRush /Курсы /Модуль 2. Java Core /Переопределение (override) методов

Переопределение (override) методов

Модуль 2. Java Core
2 уровень , 5 лекция
Открыта
Привет! Ты уже используешь методы в Java и знаешь о них многое. Как устроен механизм переопределения методов  - 1Наверняка ты сталкивался с ситуацией, когда в одном классе было много методов с одинаковым названием, но разными аргументами. Если помнишь, в тех случаях мы использовали механизм перегрузки методов. Сегодня рассмотрим другую ситуацию. Представь, что у нас есть один общий метод, но он должен делать разные вещи в зависимости от того, в каком классе он был вызван. Как реализовать такое поведение? Чтобы разобраться, возьмем родительский класс Animal, обозначающий животных, и создадим в нем метод voice — « голос»:

public class Animal {
  
   public void voice() {

       System.out.println("Голос!");
   }
}
Хотя мы только начали писать программу, потенциальная проблема тебе, скорее всего, видна: животных в мире очень много, и все «говорят» по-разному: кошки мяукают, утки крякают, змеи шипят. Как устроен механизм переопределения методов  - 2 Наша цель проста: избежать создания кучи методов для подачи голоса. Вместо того, чтобы создавать методы voiceCat() для мяуканья, voiceSnake() для шипения и т.д., мы хотим, чтобы при вызове метода voice() змея шипела, кошка мяукала, а собака лаяла. Мы легко добьемся этого с помощью механизма переопределения методов (Override в Java). Википедия дает такое пояснение термина «переопределение»: Переопределение метода (англ. Method overriding) в объектно-ориентированном программировании — одна из возможностей языка программирования, позволяющая подклассу или дочернему классу обеспечивать специфическую реализацию метода, уже реализованного в одном из суперклассов или родительских классов. Оно, в общем-то, правильное. Переопределение позволяет взять какой-то метод родительского класса и написать в каждом классе-наследнике свою реализацию этого метода. Новая реализация «заменит» родительскую в дочернем классе. Рассмотрим, как это выглядит на примере. Создадим 4 класса-наследника для нашего класса Animal:

public class Bear extends Animal {
   @Override
   public void voice() {
       System.out.println("Р-р-р!");
   }
}
public class Cat extends Animal {

   @Override
   public void voice() {
       System.out.println("Мяу!");
   }
}

public class Dog extends Animal {

   @Override
   public void voice() {
       System.out.println("Гав!");
   }
}


public class Snake extends Animal {

   @Override
   public void voice() {
       System.out.println("Ш-ш-ш!");
   }
}
Небольшой лайфхак на будущее: чтобы переопределить методы родительского класса, перейди в код класса-наследника в Intellij IDEa, нажми Ctrl+O и выбери в меню « Override methods...». Привыкай пользоваться горячими клавишами с начала, это ускоряет написание программ! Чтобы задать нужное нам поведение, мы сделали несколько вещей:
  1. Создали в каждом классе-наследнике метод с таким же названием, как и у метода в родительском классе.
  2. Сообщили компилятору, что мы не просто так назвали метод так же, как в классе-родителе: хотим переопределить его поведение. Для этого «сообщения» компилятору мы поставили над методом аннотацию @Override («переопределен»).
    Проставленная над методом аннотация @Override сообщает компилятору (да и читающим твой код программистам тоже): «Все ок, это не ошибка и не моя забывчивость. Я помню, что такой метод уже есть, и хочу переопределить его».

  3. Написали нужную нам реализацию для каждого класса-потомка. Змея при вызове voice() должна шипеть, медведь — рычать и т.д.
Давай посмотрим, как это будет работать в программе:

public class Main {

   public static void main(String[] args) {

       Animal animal1 = new Dog();
       Animal animal2 = new Cat();
       Animal animal3 = new Bear();
       Animal animal4 = new Snake();
      
       animal1.voice();
       animal2.voice();
       animal3.voice();
       animal4.voice();
   }
}
Вывод в консоль: Гав! Мяу! Р-р-р! Ш-ш-ш! Отлично, все работает как надо! Мы создали 4 переменных-ссылки родительского класса Animal, и присвоили им 4 разных объекта классов-наследников. В результате каждый объект ведет себя по-своему. Для каждого из классов-наследников переопределенный метод voice() заменил «родной» метод voice() из класса Animal (который выводит в консоль просто «Голос!»). Как устроен механизм переопределения методов  - 3 У переопределения есть ряд ограничений:
  1. У переопределенного метода должны быть те же аргументы, что и у метода родителя.

    Если метод voice родительского класса принимает на вход String, переопределенный метод в классе-потомке тоже должен принимать на вход String, иначе компилятор выдаст ошибку:

    
    public class Animal {
    
       public void voice(String s) {
    
           System.out.println("Голос! " + s);
       }
    }
    
    public class Cat extends Animal {
    
       @Override//ошибка!
       public void voice() {
           System.out.println("Мяу!");
       }
    }
    

  2. У переопределенного метода должен быть тот же тип возвращаемого значения, что и у метода родителя.

    В ином случае мы получим ошибку компиляции:

    
    public class Animal {
    
       public void voice() {
    
           System.out.println("Голос!");
       }
    }
    
    
    public class Cat extends Animal {
    
       @Override
       public String voice() {         //ошибка!
           System.out.println("Мяу!");
           return "Мяу!";
       }
    }
    

  3. Модификатор доступа у переопределенного метода также не может отличаться от «оригинального»:

    
    public class Animal {
    
       public void voice() {
    
           System.out.println("Голос!");
       }
    }
    
    public class Cat extends Animal {
    
       @Override
       private void voice() {      //ошибка!
           System.out.println("Мяу!");
       }
    }
    
Переопределение методов в Java — один из инструментов для реализации идеи полиморфизма. Поэтому главным преимуществом его использования будет та же гибкость, о которой мы говорили ранее. Мы можем выстроить простую и логичную систему классов, каждый из которых будет обладать специфическим поведением (собаки лают, кошки мяукают), но единым интерфейсом — один метод voice() на всех вместо кучи методов voiceDog(), voiceCat() и т.д.
3
Задача
Java Core, 2 уровень, 5 лекция
Недоступна
Набираем код Ӏ Java Core: 2 уровень, 5 лекция
Java Core: 2 уровень, 5 лекция. Иногда думать не надо, строчить надо! Как ни парадоксально звучит, порой пальцы «запоминают» лучше, чем сознание. Вот почему во время обучения в секретном центре JavaRush вы иногда встречаете задания на набор кода. Набирая код, вы привыкаете к синтаксису и зарабатываете немного материи. А ещё — боретесь с ленью.
Комментарии (13)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Руслан Уровень 48
12 сентября 2025
Ребят короче при переопределении можно только сужать тип возвращаемого значения и расширять модификатор доступа(Но расширять тип возвращаемого значения нельзя , как и сужать модификатор доступа) А при перегрузке можно исполнять,можно и сужать и расширять тип возвращаемого значения и можно сужать и расширять модификатор доступа,но на практике этого обычно не делают ,просто меняют сигнатуру метода и все,вот как то так!!!
Артём Уровень 112
27 сентября 2024
Здесь в комментариях дважды говорилось о противоречиях в прошлой и этой лекциях. Поскольку комментарии были написаны давно, то решил не отвечать на них, а вынести в отдельный пост для тех, кто читает комментарии и тоже задастся этим вопросом. Хочу обратить внимание, что речь в лекциях идёт о разных вещах. В прошлой лекции говорилось о перегрузке (overloading) методов, когда в одном и том же классе метод перегружается для разных типов, количества или порядка параметров. В этом случае метод выбирается во время компиляции программы. В этой лекции говорится о переопределении (overriding) методов, когда метод родительского класса переопределяется в классах-наследниках. В этом случае метод выбирается во время выполнения программы. В каждом случае справедливы свои ограничения, и ничто ничему не противоречит, а путаница возникает из-за того, что в русском языке в обоих случаях часто используют термин "переопределение метода". Я для первого случая предпочитаю всё же использовать слова "перегрузка метода", а метод называть перегруженным.
Олег Уровень 111 Expert
20 сентября 2024
Итого: Типы параметров при переопределении менять нельзя — они должны совпадать с теми, что в методе родительского класса. Возвращаемый тип можно сделать более узким (используя наследника), это ковариантный возвращаемый тип. Полиморфизм позволяет передавать объекты наследников в методы, которые принимают параметры типа родителя.
Булат Уровень 109
25 августа 2024
хорошая и понятная лекция, спасибо большое!
Владимир Кругман Уровень 51 Expert
15 августа 2023
Текущая лекция противоречит пройденной лекции в следующих утверждениях (скрины не получается добавить, поэтому текстом): I. В текущей: "2) У переопределенного метода должен быть тот же тип возвращаемого значения, что и у метода родителя. В ином случае мы получим ошибку компиляции" В пройденной лекции: "3) Сужение типа результата. В переопределенном методе мы можем поменять тип результата, сузив его." II. В текущей: "3) Модификатор доступа у переопределенного метода также не может отличаться от «оригинального»" В пройденной лекции : "2) Расширение видимости. При переопределении типа разрешается расширить видимость метода"
Дмитрий Шалаев Уровень 43 Expert
10 мая 2024
Тоже обратил внимание😀
Пахопол Юлия Уровень 51 Expert
25 октября 2022
на удивление супер понятная лекция) в отличие от предыдущей
Stas S Уровень 108 Expert
15 апреля 2022
Как на самом деле обстоят дела: - Список аргументов(параметры) должен быть точно таким же, как и для переопределённого метода. - Возвращаемый тип должен быть таким же или подтипом возвращаемого типа - Уровень доступа не может быть более строгим, чем уровень доступа переопределённого метода. - Методы, которые объявлены как final, не могут быть переопределены. - Статические методы, которые объявлены как static, не могут быть переопределены, но могут быть повторно объявлены. - Если метод нельзя наследовать, то его нельзя переопределить(например private методы).
19 ноября 2022
Спасибо, сеньор! Как там на 76-ом?
Stas S Уровень 108 Expert
21 ноября 2022
Пожалуйста. Неплохо, осваиваем БД-JDBC-HQL-Hibernate
Александр Уровень 34
20 февраля 2024
Прекрасное заключение. Лучше лекции. Спасибо.
Владимир Уровень 48 Expert
31 марта 2022
Так у переопределенных методов можно менять видимость/возвращаемое значение или нет? Во второй лекции этого же уровня Реализация абстрактных методов говорилось, что можно делать: а) Расширение видимости. При переопределении типа разрешается расширить видимость метода. б) Сужение типа результата. В переопределенном методе мы можем поменять тип результата, сузив его. А тут уже говорят, что нельзя...
Руслан Уровень 48
12 сентября 2025
можно только сужать тип возвращаемого значения и расширять модификатор доступа