@Override) в Java это возможность класса-наследника задать свою реализацию метода, уже описанного в родительском классе. При вызове метода у объекта выполняется версия из его собственного класса, а не из родителя. Так реализуется полиморфизм: один метод voice() ведет себя по-разному у Dog, Cat и Snake.Кратко
- Переопределение (
@Override) это своя реализация родительского метода в классе-наследнике. - При вызове метода у объекта выполняется версия из его класса, а не из родителя.
- Аннотация
@Overrideне обязательна, но защищает от ошибок: компилятор проверит, что метод действительно переопределяет родительский. - У переопределенного метода те же имя и аргументы, совместимый тип возврата и не более строгий модификатор доступа, чем у родителя.
- Переопределение (override) отличается от перегрузки (overload): перегрузка это методы с одним именем, но разными аргументами.
- Переопределение это основной инструмент полиморфизма.
Animal, обозначающий животных, и создадим в нем метод voice: «голос»:
public class Animal {
public void voice() {
System.out.println("Голос!");
}
}
Хотя мы только начали писать программу, потенциальная проблема тебе, скорее всего, видна: животных в мире очень много, и все «говорят» по-разному: кошки мяукают, утки крякают, змеи шипят.
Наша цель проста: избежать создания кучи методов для подачи голоса. Вместо того, чтобы создавать методы voiceCat() для мяуканья, voiceSnake() для шипения и т.д., мы хотим, чтобы при вызове метода voice() змея шипела, кошка мяукала, а собака лаяла.
Что такое переопределение метода
Мы легко добьемся этого с помощью механизма переопределения методов (Override в Java). Википедия дает такое пояснение термина «переопределение»: Переопределение метода (англ. Method overriding) в объектно-ориентированном программировании, одна из возможностей языка программирования, позволяющая подклассу или дочернему классу обеспечивать специфическую реализацию метода, уже реализованного в одном из суперклассов или родительских классов. Оно, в общем-то, правильное. Переопределение позволяет взять какой-то метод родительского класса и написать в каждом классе-наследнике свою реализацию этого метода. Новая реализация «заменит» родительскую в дочернем классе.Как переопределить метод (@Override)
Рассмотрим, как это выглядит на примере. Создадим 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...». Привыкай пользоваться горячими клавишами с начала, это ускоряет написание программ!
Чтобы задать нужное нам поведение, мы сделали несколько вещей:
- Создали в каждом классе-наследнике метод с таким же названием, как и у метода в родительском классе.
Сообщили компилятору, что мы не просто так назвали метод так же, как в классе-родителе: хотим переопределить его поведение. Для этого «сообщения» компилятору мы поставили над методом аннотацию @Override («переопределен»).
Проставленная над методом аннотация @Override сообщает компилятору (да и читающим твой код программистам тоже): «Все ок, это не ошибка и не моя забывчивость. Я помню, что такой метод уже есть, и хочу переопределить его».- Написали нужную нам реализацию для каждого класса-потомка. Змея при вызове
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 (который выводит в консоль просто «Голос!»).

Ограничения переопределения
У переопределения есть ряд ограничений:У переопределенного метода должны быть те же аргументы, что и у метода родителя.
Если метод
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("Мяу!"); } }У переопределенного метода должен быть тот же тип возвращаемого значения, что и у метода родителя.
Точнее, тип возврата может быть таким же или его подтипом (это называется ковариантным возвратом и работает начиная с Java 5). А вот несовместимые типы (например, void и String) вызовут ошибку компиляции:
public class Animal { public void voice() { System.out.println("Голос!"); } } public class Cat extends Animal { @Override public String voice() { //ошибка! System.out.println("Мяу!"); return "Мяу!"; } }Модификатор доступа у переопределенного метода можно сделать таким же или более открытым, но нельзя сузить.
Например, заменить
publicнаprivateнельзя, это вызовет ошибку:public class Animal { public void voice() { System.out.println("Голос!"); } } public class Cat extends Animal { @Override private void voice() { //ошибка! System.out.println("Мяу!"); } }
voice() на всех вместо кучи методов voiceDog(), voiceCat() и т.д.Переопределение и перегрузка: в чем разница
Их часто путают, поэтому сведем различия в таблицу:| Признак | Переопределение (override) | Перегрузка (overload) |
|---|---|---|
| Что это | своя реализация метода родителя в наследнике | несколько методов с одним именем в одном классе |
| Имя метода | совпадает с родительским | одинаковое у всех перегрузок |
| Аргументы | такие же, как у родителя | разные (число или типы) |
| Где | родитель и наследник (наследование) | обычно один класс |
| Аннотация | @Override (рекомендуется) | не нужна |
| Связь с ООП | полиморфизм | удобство вызова |
Читайте также
- Принципы ООП: полиморфизм, ради которого и нужно переопределение.
- Отношения между классами: наследование, на котором держится переопределение.
- Модификаторы доступа: почему нельзя сузить доступ при переопределении.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ