Ответ
Переопределение методов (method overriding) — это механизм в объектно-ориентированном программировании, позволяющий дочернему классу предоставить свою, специфическую реализацию метода, который уже определён в его родительском классе.
Условия корректного переопределения в Java:
- Та же сигнатура: Имя метода, тип, количество и порядок параметров должны совпадать.
- Совместимый возвращаемый тип: В Java 5+ допускается ковариантный возвращаемый тип (можно вернуть подтип исходного типа).
- Нельзя сужать модификатор доступа: Доступ в дочернем классе должен быть таким же или шире (public → public, protected → public/protected).
- Нельзя добавлять новые проверяемые исключения: Можно добавлять только непроверяемые (unchecked) или сужать тип исключений.
Пример переопределения:
class Animal {
// Метод, который будет переопределён
public void makeSound() {
System.out.println("The animal makes a sound");
}
protected String getFood() {
return "some food";
}
}
class Dog extends Animal {
// Переопределение метода makeSound()
@Override // Аннотация @Override — строгая рекомендация для проверки компилятором
public void makeSound() {
System.out.println("The dog barks: Woof!");
}
// Ковариантный возвращаемый тип (String — подтип Object)
@Override
public String getFood() {
return "dog food";
}
}
Что нельзя переопределить?
finalметоды.staticметоды (это скрытие метода, а не переопределение).privateметоды (они не видны в дочернем классе).
Зачем это нужно? Переопределение — основа полиморфизма времени выполнения (runtime polymorphism). Оно позволяет объектам дочерних классов отвечать на одни и те же вызовы методов по-разному, что делает код гибким и расширяемым.
Ответ 18+ 🔞
А, слушай, вот эта тема с переопределением методов — это ж классика, блядь, основа основ, но народ её так часто в говне пережёвывает, что аж тошно становится. Сейчас разжуем, как надо.
Представь, есть у тебя класс Animal, как бы общий предок, с методом makeSound(). Ну, животное и животное, издаёт какой-то звук. А теперь появляется его отпрыск, класс Dog. И этот пёс, блядь, не хочет мычать как попало, он хочет гавкать конкретно! Вот он и переопределяет родительский метод. То есть говорит: «Отстань, папаша, я сам знаю, как мне звучать!».
Но, сука, не всё так просто! Нельзя просто взять и переписать метод как вздумается. Есть правила, нарушишь — компилятор тебе ебальник набьёт.
Правила, блядь, железные:
- Сигнатура — святое. Имя метода, количество и тип параметров — должны совпадать один в один. Хочешь добавить лишний параметр? Иди нахуй, это будет уже другой метод, а не переопределение.
- Возвращаемый тип можно только уточнить. В старые добрые времена он тоже должен был совпадать. А щас, ёпта, можно вернуть не просто
Object, а его конкретного сына, напримерString. Это называется ковариантный возвращаемый тип. Умное слово, но суть простая: можно быть точнее, но не более размытым. - Модификатор доступа — только шире. Если у родителя метод
protected, то в потомке можно сделатьpublic. А вот наоборот —publicсузить доprotected— низя, блядь! Это же пиздец, все, кто раньше имел доступ, вдруг его лишатся. Полный пиздец и нарушение контракта. - С исключениями — не наглей. Нельзя в переопределённом методе объявлять новые проверяемые исключения (checked exceptions), которых не было у родителя. Это тоже пиздец для того, кто код использует. А вот объявить меньше исключений или только непроверяемые (unchecked) — пожалуйста, на здоровье.
Смотри, как это выглядит в коде, тут всё честно:
class Animal {
// Метод, который все будут переписывать
public void makeSound() {
System.out.println("The animal makes a sound");
}
protected String getFood() {
return "some food";
}
}
class Dog extends Animal {
// ВОТ ОНО, ПЕРЕОПРЕДЕЛЕНИЕ!
@Override // Эта аннотация — твой друг. Она кричит компилятору: «Чувак, проверь, я тут правильно переопределяю?». Если накосячил — получишь ошибку сразу, а не хуй пойми что в рантайме.
public void makeSound() {
System.out.println("The dog barks: Woof!");
}
// И тут переопределение, с ковариантным типом
@Override
public String getFood() {
return "dog food";
}
}
А теперь, блядь, что НИКОГДА не переопределяется:
finalметоды. Ониfinalнахуй, точка. Приехали.staticметоды. Это вообще не переопределение, а сокрытие метода. Совсем другая история, не путай, а то в рот меня чих-пых!privateметоды. Они видны только внутри своего класса. Какой нахуй потомок? Он про них нихуя не знает.
Зачем весь этот цирк? А это и есть та самая магия полиморфизма времени выполнения, ёба! Ты создаёшь объект Dog, но ссылаешься на него как на Animal. И когда вызываешь animal.makeSound(), система смотрит: «Ага, объект-то на самом деле пёс!» — и запускает переопределённый метод из Dog. Не тот общий, а конкретно собачий. Это и есть гибкость, расширяемость и вся мощь ООП. Если, конечно, не ебашить в него с ноги, нарушая правила.