Одним из ключевых аспектов объектно-ориентированного программирования является наследование. С помощью наследования можно расширить функционал уже имеющихся классов за счет добавления нового функционала или изменения старого. Например, имеется следующий класс Person, описывающий отдельного человека:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class Person { private String name; private String surname; public String getName() { return name; } public String getSurname() { return surname; } public Person(String name, String surname){ this.name=name; this.surname=surname; } public void displayInfo(){ System.out.println("Имя: " + name + " Фамилия: " + surname); } } |
И, возможно, впоследствии мы решили расширить имеющуюся систему и классов, добавив в нее класс, описывающий сотрудника предприятия — класс Employee. Так как этот класс реализует тот же функционал, что и класс Person, так как сотрудник — это также и человек, то было бы рационально сделать класс Employee производным (или наследником) от класса Person, который, в свою очередь, называется базовым классом или родителем:
1 2 3 |
class Employee extends Person{ } |
Чтобы объявить один класс наследником от другого, надо использовать после имени класса-наследника ключевое слово extends, после которого идет имя базового класса. Для класса Employee базовым является Person, и поэтому класс Employee наследует все те же поля и методы, которые есть в классе Person.
В классе Employee могут быть определены свои методы и поля, а также конструктор. Способность к изменению функциональности, унаследованной от базового класса, назвывается полиморфизмом и является одинм из ключевых аспектов объектно-ориентированного программирования наряду с наследованием и инкапсуляцией.
Например, переопределим метод displayInfo()
класса Person в классе Employee:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Employee extends Person{ private String company; public Employee(String name, String surname, String company) { super(name, surname); this.company=company; } public void displayInfo(){ super.displayInfo(); System.out.println("Компания: " + company); } } |
Класс Employee определяет дополнительное поле для хранения компании, в которой работает сотрудник. Кроме того, оно также устанавливается в конструкторе.
Так как поля name и surname в базовом классе Person объявлены с модификатором private, то мы не можем к ним напрямую обратиться из класса Employee. Однако в данном случае нам это не нужно. Чтобы их установить, мы обращаемся к конструктору базового класса с помощью ключевого слова super, в скобках после которого идет перечисление передаваемых аргументов.
С помощью ключевого слова super
мы можем обратиться к любому члену базового класса — методу или полю, если они не определены с модификатором private.
Также в классе Employee переопределяется метод displayInfo()
базового класса. В нем с помощью ключевого super
также идет обращение к методу displayInfo(), но уже базового класса, и затем выводится дополнительная информация, относящаяся только к Employee.
Используя обращение к методом базового класса, можно было бы переопределить метод displayInfo()
следующим образом:
1 2 3 4 |
public void displayInfo(){ System.out.println("Имя: " + super.getName() + " Фамилия: " + super.getSurname() + " Компания: " + company); } |
При этом нам необязательно переопределять все методы базового класса. Например, в данном случае мы не переопределяем методы getName()
и getSurname()
. Поэтому для этих методов класс-наследник будет использовать реализацию из базового класса. И в основной программе мы можем эти методы использовать:
1 2 3 4 5 6 7 |
public static void main(String[] args) { Employee empl = new Employee("Tom", "Simpson", "Oracle"); empl.displayInfo(); String firstName = empl.getName(); System.out.println(firstName); } |
Запрет наследования
Хотя наследование очень интересный и эффективный механизм, но в некоторых ситуациях его применение может быть нежелательным. И в этом случае можно запретить наследование с помощью ключевого слова final
. Например:
1 2 |
public final class Person { } |
Если бы класс Person был бы определен таким образом, то следующий код был бы ошибочным и не сработал, так как мы тем самым запретили наследование:
1 2 |
class Employee extends Person{ { } |
Кроме запрета наследования можно также запретить переопределение отдельных методов. Например, в примере выше переопределен метод displayInfo()
, запретим его переопределение:
1 2 3 4 5 6 7 8 |
public class Person { //........................ public final void displayInfo(){ System.out.println("Имя: " + name + " Фамилия: " + surname); } } |
В этом случае в классе Employee надо будет создать метод с другим именем для вывода информации об объекте.