Наследование
Общее поведение без копирования
Представь несколько классов, у которых есть общая основа. Например, собака и кошка — разные животные,
но обоим могут быть нужны имя и действие eat().
Вместо того чтобы повторять один и тот же код в каждом классе, общую часть можно вынести в родительский класс,
а более конкретный класс сделать его наследником.
class Animal {
private final String name;
Animal(String name) {
this.name = name;
}
String name() {
return name;
}
void eat() {
System.out.println(name + " ест");
}
}
class Dog extends Animal {
Dog(String name) {
super(name);
}
void bark() {
System.out.println(name() + " лает");
}
}
Ключевое слово extends говорит, что Dog
наследуется от Animal. Поэтому у объекта Dog
есть не только собственный метод bark(), но и доступное ему поведение родительского класса:
Dog dog = new Dog("Рекс");
dog.eat();
dog.bark();
Наследование удобно читать как отношение «является»: Dog является
Animal, но при этом добавляет более конкретные возможности.
Родительский класс обычно называют superclass, а наследника — subclass.
Что именно получает наследник
Наследник может использовать доступные ему поля и методы родительского класса и добавлять свои.
Но модификаторы доступа продолжают действовать. Например, поле name
в примере объявлено как private, поэтому Dog
не может обратиться к нему напрямую. Вместо этого он использует доступный метод name().
Конструкторы при этом не наследуются. Когда создаётся объект дочернего класса, должна быть выполнена
инициализация его родительской части. В примере конструктор Dog
передаёт имя конструктору Animal через super(name).
Dog(String name) {
super(name);
}
Если у родительского класса есть подходящий конструктор без аргументов, явный вызов
super() часто писать не требуется. Если такого конструктора нет,
нужно вызвать подходящий конструктор родителя самостоятельно.
У класса только один прямой родитель
Обычный Java-класс может напрямую наследоваться только от одного другого класса.
Например, запись class Dog extends Animal допустима,
а указать после extends сразу два класса нельзя.
При этом цепочка может быть длиннее: один класс наследуется от другого, тот — от третьего и так далее.
Наследование должно описывать смысл
Наследование стоит использовать не просто потому, что хочется переиспользовать несколько строк кода.
Оно хорошо подходит, когда дочерний класс действительно является более конкретным вариантом родительского.
Dog — это Animal, поэтому такая связь естественна.
Итак, наследование позволяет вынести общую часть в родительский класс, а в дочернем оставить только то, что делает его более конкретным. На следующей теме эта связь между родительским и дочерним классами станет основой для ещё одной важной возможности ООП.