Переопределение методов. Аннотация @Override
Переопределение метода (англ. Method overriding) — это возможность реализовать метод так, чтобы он имел идентичную сигнатуру с методом класса-предка, но предоставлял иное поведение, не вызывая коллизий при его использовании. Так же это один из инструментов реализации полиморфизма.
То есть, у нас может быть во всех классах потомках метод, у которого совпадают с методом класса-предка такие параметры: имя, перечень аргументов, возвращаемый тип, модификатор доступа. Отличия будут только в внутренней реализации этих методов. А использование таких одноименных методов не приведет к коллизии как, например, в случае с одноименными полями в методах и классах.
Переопределяя метод — обязательно помечайте его аннотацией @Override! Это не только спасет от потенциальных ошибок, но и повысит читаемость кода.
Переопределять можно только нестатические методы.
Рассмотрим это на примере классов пакета view части проекта "XO".
Вот тут можно скачать репу с кодом из примеров.
Для начала, создадим вот такие классы:
package xo.view;
public class View {
String name = "It is a view.";
public void show() {
System.out.println("Метод класса View.");
}
}
package xo.view;
public class SecondView extends View {
String name = "It is a secondView.";
@Override
public void show() {
System.out.println("Сообщение из класса SecondView.");
}
}
package xo.view;
public final class ThirdView extends SecondView {
String name = "It is a thirdView.";
@Override
public void show() {
System.out.println("Сообщение из класса ThirdView.");
}
}
Благодаря @Override мы явно указываем, что метод show не просто какой-то локальный метод, а что это такой же метод, как и в классе-предке, но с новым поведением.
В данном случае — сообщением.
И что это нам дает?
Переопределение методов дает возможность вызывать методы классов-наследников через ссылки классов предков. Что кажется противоречит принципу доступности членов классов в наследовании в java (потомкам доступны свойства предков, а не наоборот).
Напомним, невозможность вызывать члены потомка из предка, на примере следующего кода:
package xo;
import xo.view.SecondView;
import xo.view.ThirdView;
import xo.view.View;
public class Main {
public static void main(String[] args) {
View view = new View();
printName(view);
SecondView secondView = new SecondView();
printName(secondView);
ThirdView thirdView = new ThirdView();
printName(thirdView);
}
private static void printName(View someView) {
System.out.println(someView.name);
}
}
Результатом работы трех вызовов метода printName будет одна и та же надпись: It is a view.. Копируйте код, пробуйте!
Потому что ссылка someView ограничена типом View и не может получить доступ к полям потомков класса View.
Несмотря на идентичность имен этих полей.
А как вызвать метод потомка из ссылки типа предка?
Просто вызвать, если он там переопределен. Перепишем пример вызовов так:
package xo;
import xo.view.SecondView;
import xo.view.ThirdView;
import xo.view.View;
public class Main {
public static void main(String[] args) {
View view = new View();
callMethod(view);
SecondView secondView = new SecondView();
callMethod(secondView);
ThirdView thirdView = new ThirdView();
callMethod(thirdView);
}
private static void callMethod(View someView) {
someView.show();
}
}
Теперь на экран будет выведено три различных сообщения: Метод класса View., Сообщение из класса SecondView., Сообщение из класса ThirdView..
Как это работает?
Даже в байткоде скомпилированной программы будет происходить вызов метода show у типа View.
Но в момент исполнения этого байткода JVM получит ссылку на конкретный объект и полезет смотреть его перечень методов (vtable).
В этой таблице находятся ссылки на все методы текущего объекта.
И, в нашем случае, метод show там будет, даже если бы мы его не переопределили ни в одном из потомков.
Если бы мы show не переопределили ни в одном из потомков, то в vtable их объектов (инстансов) была бы ссылка на один и тот же метод show класса View.
Но, поскольку мы таки переопределили метод show, то в vtable каждого объекта класса-потомка будет ссылка не на родительский метод show, а на свой локальный переопределенный метод show.
Вот так java понимает какой именно метод стоит вызывать в работе с переопределением методов.
В предыдущем уроке было сказано, что все классы в мире java прямо или опосредовано наследуются от класса Object.
И Вы самостоятельно можете провести эксперименты в коде, по этой теме.
В файлах, из выше упомянутого репозитория, Вы можете найти класс Board с переопределенным методом класса Object — toString().
Попробуйте реализовать свои toString() для остальных классов, протестируйте их.
Так же попробуйте добавить в View-классы методы gеtName() и вызвать их из класса Main.
Полезные ссылки
- Inheritance
- Overriding