Перейти к содержанию
Educora
Средний17 мин8 / 18

Наследование и полиморфизм

Расширяй существующий класс с помощью extends, используй super и @Override, узнай, как работают полиморфизм и instanceof.

Проверь себя
В этом уроке ты узнаешь
  • Создавать класс-наследник с помощью extends и super
  • Переопределять метод с аннотацией @Override
  • Объяснять полиморфизм и проверять тип с помощью instanceof

В компании работают обычные сотрудники и менеджеры. У всех есть имя и зарплата, но менеджер получает ещё и бонус. Вместо того чтобы писать класс Manager с нуля и повторять весь код Employee, можно сказать: «Менеджер — это тоже сотрудник, просто немного особенный». Для этого и существует наследование.

extends и super

Запись class Manager extends Employee означает, что Manager наследуется от Employee: подкласс (subclass) получает поля и методы суперкласса (superclass). Конструктор подкласса должен сначала вызвать конструктор родителя через super(...). А super.salary() вызывает родительскую версию метода.

Java
public class Main {
    public static void main(String[] args) {
        Employee e = new Employee("Nigar", 1200);
        Manager m = new Manager("Rashad", 2000, 500);
        System.out.println(e.describe());
        System.out.println(m.describe());
        System.out.println(m.getName() + " earns " + m.salary());
    }
}

class Employee {
    protected String name;
    protected double baseSalary;

    Employee(String name, double baseSalary) {
        this.name = name;
        this.baseSalary = baseSalary;
    }

    String getName() { return name; }

    double salary() { return baseSalary; }

    String describe() {
        return name + ": " + salary() + " AZN";
    }
}

class Manager extends Employee {
    private double bonus;

    Manager(String name, double baseSalary, double bonus) {
        super(name, baseSalary);
        this.bonus = bonus;
    }

    @Override
    double salary() {
        return super.salary() + bonus;
    }
}
Ожидаемый результат
Nigar: 1200.0 AZN
Rashad: 2500.0 AZN
Rashad earns 2500.0
  • В классе Manager нет методов getName() и describe(), но они работают — они унаследованы от родителя.
  • Метод salary() переопределён (override): то же имя и параметры, но новое поведение.
  • Обрати внимание: describe() написан в родителе, но для менеджера показал 2500. Когда он вызывает salary(), работает версия фактического класса объекта.
  • protected-поля видны наследникам, но не внешнему коду (из других пакетов).

Полиморфизм

Полиморфизм («многоформенность») означает, что переменная родительского типа может ссылаться на объект любого подкласса, а при вызове метода работает версия фактического класса объекта. Ниже в массиве Shape лежат круги и прямоугольники, и цикл вызывает area(), не разбираясь, где что, — каждая фигура считает свою площадь по-своему.

Java
public class Main {
    static double round2(double x) {
        return Math.round(x * 100) / 100.0;
    }

    public static void main(String[] args) {
        Shape[] shapes = { new Circle(1), new Rectangle(3, 4), new Circle(2) };
        double total = 0;
        for (Shape s : shapes) {
            System.out.println(s.name() + ": " + round2(s.area()));
            total += s.area();
        }
        System.out.println("Total: " + round2(total));
    }
}

class Shape {
    String name() { return "Shape"; }
    double area() { return 0; }
}

class Circle extends Shape {
    private double r;
    Circle(double r) { this.r = r; }
    @Override String name() { return "Circle"; }
    @Override double area() { return Math.PI * r * r; }
}

class Rectangle extends Shape {
    private double w, h;
    Rectangle(double w, double h) { this.w = w; this.h = h; }
    @Override String name() { return "Rectangle"; }
    @Override double area() { return w * h; }
}
Ожидаемый результат
Circle: 3.14
Rectangle: 12.0
Circle: 12.57
Total: 27.71
Интерактив
Загрузка симуляции…
Меняй радиус круга и сравнивай площадь с формулой из класса Circle — π · r². Выбери и другие фигуры: для каждой из них можно было бы написать свой подкласс.

Сила полиморфизма в том, что если завтра добавить класс Triangle, цикл в main менять не придётся. Код зависит от общего типа (Shape), а не от конкретных классов. Именно так растут большие программы.

Проверка типа с instanceof

Иногда нужно узнать конкретный тип объекта. Выражение o instanceof String s проверяет, является ли o строкой, и если да, сразу помещает его в переменную s типа String. Такая запись (pattern matching) доступна начиная с Java 16.

Java
public class Main {
    public static void main(String[] args) {
        Object[] things = { "Baku", 42, new Dog("Rex"), 3.5 };
        for (Object o : things) {
            if (o instanceof String s) {
                System.out.println("String of length " + s.length());
            } else if (o instanceof Dog d) {
                d.bark();
            } else {
                System.out.println("Something else: " + o);
            }
        }
    }
}

class Dog {
    private String name;
    Dog(String name) { this.name = name; }
    void bark() { System.out.println(name + " says Woof!"); }
}
Ожидаемый результат
String of length 4
Something else: 42
Rex says Woof!
Something else: 3.5

Главное

  • class B extends A — B наследует поля и методы A; в Java класс может наследоваться только от одного класса.
  • super(...) вызывает конструктор родителя, а super.method() — метод родителя.
  • @Override заставляет компилятор проверить, что метод действительно заменяет метод родителя.
  • Полиморфизм: переменная родительского типа ссылается на объект подкласса, а срабатывает метод фактического класса объекта.
  • o instanceof Type t проверяет тип и сразу кладёт объект в новую переменную.

Проверь себя

Вопросов: 10. Каждый правильный ответ приносит XP.

1 / 10
Как вызвать конструктор родителя из конструктора подкласса?