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

Абстрактные классы и интерфейсы

Абстрактные классы и методы, интерфейсы, implements, методы по умолчанию и сортировка с интерфейсом Comparable.

Проверь себя
В этом уроке ты узнаешь
  • Писать abstract-класс и метод и реализовывать их в подклассах
  • Объявлять интерфейс и реализовывать несколько интерфейсов в одном классе
  • Выбирать между абстрактным классом и интерфейсом

Вернёмся к классу Shape из прошлого урока: его метод area() возвращал 0. Но что такое «просто фигура» и почему её площадь равна нулю? На самом деле создавать new Shape() вообще бессмысленно — Shape лишь общая идея. Для выражения таких идей в Java есть два инструмента: абстрактные классы и интерфейсы.

Абстрактные классы

Из класса, объявленного abstract, нельзя создать объект — он служит только основой для подклассов. В абстрактном классе могут быть абстрактные методы: у них есть заголовок, но нет тела. Каждый подкласс обязан реализовать эти методы. При этом в абстрактном классе могут быть и обычные поля, конструкторы и готовые методы.

Java
public class Main {
    public static void main(String[] args) {
        Payment[] payments = { new CardPayment(50), new CashPayment(20) };
        for (Payment p : payments) {
            p.process();
        }
    }
}

abstract class Payment {
    protected double amount;

    Payment(double amount) { this.amount = amount; }

    abstract double fee();

    void process() {
        System.out.println(getClass().getSimpleName() + ": " + amount + " + fee " + fee() + " = " + (amount + fee()));
    }
}

class CardPayment extends Payment {
    CardPayment(double amount) { super(amount); }
    @Override double fee() { return amount * 0.02; }
}

class CashPayment extends Payment {
    CashPayment(double amount) { super(amount); }
    @Override double fee() { return 0; }
}
Ожидаемый результат
CardPayment: 50.0 + fee 1.0 = 51.0
CashPayment: 20.0 + fee 0.0 = 20.0

Класс Payment знает, как обрабатывается платёж (метод process), но не знает, какова комиссия, — это сообщает каждый вид платежа сам. getClass().getSimpleName() возвращает имя фактического класса объекта. Если написать new Payment(10), компилятор выдаст ошибку: Payment is abstract; cannot be instantiated.

Интерфейсы

Определение
Интерфейс

Контракт, описывающий, что умеет делать класс: список методов, обычно без тела. Класс реализует интерфейс с помощью ключевого слова implements и обязан написать все его абстрактные методы.

Класс может наследоваться только от одного класса, но реализовывать сколько угодно интерфейсов. Утка умеет и летать, и плавать, а самолёт — только летать, хотя общего родителя у них нет. Начиная с Java 8 в интерфейсе можно писать и методы по умолчанию (default) с телом: класс может использовать их как есть или переопределить.

Java
public class Main {
    public static void main(String[] args) {
        Duck duck = new Duck();
        Plane plane = new Plane();
        Flyable[] flyers = { duck, plane };
        for (Flyable f : flyers) {
            f.fly();
            f.land();
        }
        duck.swim();
    }
}

interface Flyable {
    void fly();

    default void land() {
        System.out.println("  ...landing safely");
    }
}

interface Swimmable {
    void swim();
}

class Duck implements Flyable, Swimmable {
    public void fly() { System.out.println("Duck flaps its wings"); }
    public void swim() { System.out.println("Duck swims in the lake"); }
}

class Plane implements Flyable {
    public void fly() { System.out.println("Plane starts its engines"); }
    @Override
    public void land() { System.out.println("  ...landing at Heydar Aliyev Airport"); }
}
Ожидаемый результат
Duck flaps its wings
  ...landing safely
Plane starts its engines
  ...landing at Heydar Aliyev Airport
Duck swims in the lake

Comparable: интерфейс стандартной библиотеки

Собственная библиотека Java тоже построена на интерфейсах. Arrays.sort() умеет сортировать числа и строки, а как быть со своими объектами? Если класс реализует интерфейс Comparable<Product> и пишет метод compareTo, Arrays.sort() сможет отсортировать и его. Если compareTo возвращает отрицательное число, текущий объект идёт раньше; положительное — позже; 0 означает равенство.

Java
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        Product[] cart = {
            new Product("Laptop", 1500),
            new Product("Pen", 1.2),
            new Product("Backpack", 45)
        };
        Arrays.sort(cart);
        System.out.println(Arrays.toString(cart));
    }
}

class Product implements Comparable<Product> {
    private final String name;
    private final double price;

    Product(String name, double price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public int compareTo(Product other) {
        return Double.compare(this.price, other.price);
    }

    @Override
    public String toString() { return name + " " + price; }
}
Ожидаемый результат
[Pen 1.2, Backpack 45.0, Laptop 1500.0]

Что выбрать?

ПризнакАбстрактный классИнтерфейс
Ключевое словоextendsimplements
Сколько можно использовать?только одинсколько угодно
Полялюбые полятолько константы
Конструкторможет бытьнет
Смысл«является» (оплата картой является платежом)«умеет» (утка умеет летать)

Главное

  • Объект abstract-класса создать нельзя; его абстрактные методы реализуют подклассы.
  • Интерфейс — контракт о том, что умеет класс; класс выполняет его через implements.
  • Класс может наследовать один класс, но реализовывать много интерфейсов.
  • У методов default есть готовое тело прямо в интерфейсе.
  • Comparable и compareTo позволяют сортировать свои объекты с помощью Arrays.sort().

Проверь себя

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

1 / 10
Какая строка не скомпилируется для abstract class Animal?