Переопределение методов. Аннотация @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
.
Вам ответят команда поддержки Хекслета или другие студенты.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Наши выпускники работают в компаниях:
Зарегистрируйтесь или войдите в свой аккаунт