- Писать
abstract-класс и метод и реализовывать их в подклассах - Объявлять интерфейс и реализовывать несколько интерфейсов в одном классе
- Выбирать между абстрактным классом и интерфейсом
Вернёмся к классу Shape из прошлого урока: его метод area() возвращал 0. Но что такое «просто фигура» и почему её площадь равна нулю? На самом деле создавать new Shape() вообще бессмысленно — Shape лишь общая идея. Для выражения таких идей в Java есть два инструмента: абстрактные классы и интерфейсы.
Абстрактные классы
Из класса, объявленного abstract, нельзя создать объект — он служит только основой для подклассов. В абстрактном классе могут быть абстрактные методы: у них есть заголовок, но нет тела. Каждый подкласс обязан реализовать эти методы. При этом в абстрактном классе могут быть и обычные поля, конструкторы и готовые методы.
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) с телом: класс может использовать их как есть или переопределить.
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 означает равенство.
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]
Что выбрать?
| Признак | Абстрактный класс | Интерфейс |
|---|---|---|
| Ключевое слово | extends | implements |
| Сколько можно использовать? | только один | сколько угодно |
| Поля | любые поля | только константы |
| Конструктор | может быть | нет |
| Смысл | «является» (оплата картой является платежом) | «умеет» (утка умеет летать) |
Главное
- Объект
abstract-класса создать нельзя; его абстрактные методы реализуют подклассы. - Интерфейс — контракт о том, что умеет класс; класс выполняет его через
implements. - Класс может наследовать один класс, но реализовывать много интерфейсов.
- У методов
defaultесть готовое тело прямо в интерфейсе. ComparableиcompareToпозволяют сортировать свои объекты с помощьюArrays.sort().
Проверь себя
Вопросов: 10. Каждый правильный ответ приносит XP.
abstract class Animal?