Перейти к основному содержимому
Модуль 3 · Урок 2 из 26/14 в курсе~15 min
Уроки модуля (2/2)

Полиморфизм и абстрактные классы

Полиморфизм и абстрактные классы — это продвинутые концепции объектно-ориентированного программирования, которые позволяют нам писать гибкий, расширяемый код, независимый от конкретных реализаций.

Полиморфизм

Термин полиморфизм (множество форм) относится к способности обращаться с объектами разных классов так, как если бы они принадлежали к одному типу. В Java ссылочная переменная суперкласса может хранить ссылку на объект любого из его подклассов.

Java
class Animal {
    public void makeSound() {
        System.out.println("Verso...");
    }
}

class Dog extends Animal {
    public void makeSound() {
        System.out.println("Woof");
    }
}

class Cat extends Animal {
    public void makeSound() {
        System.out.println("Miao");
    }
}

Благодаря полиморфизму мы можем сделать это:

Java
Animal myAnimal1 = new Dog(); // Polymorphism
Animal myAnimal2 = new Cat(); // Polymorphism

myAnimal1.makeSound(); // Executes Dog's method (Woof)
myAnimal2.makeSound(); // Executes Cat's method (Miao)

Решение о том, какой метод вызывать, принимается во время выполнения (позднее связывание или динамическая отправка) на основе фактического объекта, а не типа ссылочной переменной.

Абстрактные классы (abstract)

Абстрактный класс — это класс, отмеченный ключевым словом abstract, экземпляр которого невозможно создать напрямую (вы не можете выполнить new MyAbstractClass()). Он служит «частичным шаблоном» для других классов.

Абстрактные методы

Абстрактный класс может содержать абстрактные методы: методы, объявленные без тела (без фигурных скобок и кода), заканчивающиеся точкой с запятой. Неабстрактные подклассы необходимы для реализации всех унаследованных абстрактных методов.

Java
abstract class Shape {
    String color;

    // Abstract method (no body)
    public abstract double getArea();
}

class Circle extends Shape {
    double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    // Required to implement getArea
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

Динамическое связывание/Позднее связывание

Как Java узнает, какой метод выполнять во время выполнения? Этот процесс известен как Позднее связывание (или динамическое связывание). В отличие от перегрузки (которая решается во время компиляции), компилятор не знает, какой конкретный метод будет выполнен. Вместо этого он генерирует общую инструкцию вызова. Во время выполнения виртуальная машина Java (JVM) проверяет реальный объект в памяти и вызывает реализацию метода, определенную в классе этого объекта.

Абстрактные классы и недостающая реализация

Если подкласс наследуется от абстрактного класса, у него есть два варианта:

  1. Реализовать все абстрактные методы: в этом случае подкласс может быть обычным (конкретным) классом и может быть создан экземпляр.
  2. Объявить себя абстрактным: если подкласс не предоставляет реализацию для всех унаследованных абстрактных методов, его также необходимо объявить с ключевым словом abstract.
Java
abstract class Animal {
    public abstract void makeSound();
}

// This class MUST be abstract because it does not implement makeSound()
abstract class Canine extends Animal {
    // Inherits makeSound() but does not implement it
}

Попробуйте сами

Упражнение#java.m3.l2.e1
Попыток: 0Загрузка…

Объявите переменную s типа Shape и присвойте ей новый объект Circle, используя полиморфизм.

Загрузка редактора…
Показать подсказку

Напишите `Shape s = new Circle();`, чтобы использовать базовый тип в качестве ссылочного типа.

Решение доступно после 3 попыток

Упражнение#java.m3.l2.e2
Попыток: 0Загрузка…

Сделайте класс Shape абстрактным и добавьте абстрактный метод double getArea(). Затем доработайте Square, чтобы он расширял Shape и реализовывал getArea(), возвращающий сторону * сторону.

Загрузка редактора…
Показать подсказку

Объявите `public Abstract Double getArea();` в Shape. В Square добавьте `@Override public double getArea() { returnside *side; }`.

Решение доступно после 3 попыток

Упражнение#java.m3.l2.e3
Попыток: 0Загрузка…

Объявите в основном методе массив типа Shape[], содержащий объект Circle с радиусом 2,0 и объект Square со стороной 3,0. Затем используйте цикл (for или for-each), чтобы напечатать площадь каждой фигуры, вызвав метод getArea().

Загрузка редактора…
Показать подсказку

Объявите массив с помощью `Shape[] shape = { new Circle(2.0), new Square(3.0) };` и пройдите по нему, используя `for (Shape s : shapes)`, напечатав `s.getArea()`.

Решение доступно после 3 попыток