Menu
Coddy logo textTech

Sealed-классы (Java 17+)

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 52 из 87.

В традиционном Java, когда вы создаёте class, любой другой class может расширить его (если только вы не пометите его как final). Но что, если вы хотите разрешить наследование, только не от кого угодно? В Java 17 появились запечатанные classes, чтобы предоставить вам точный контроль над тем, какие classes могут расширять ваш class.

Запечатанный класс явно объявляет свои разрешённые подклассы с помощью ключевых слов sealed и permits:

sealed abstract class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

final class Circle extends Shape {
    double radius;
    double area() { return Math.PI * radius * radius; }
}

final class Rectangle extends Shape {
    double width, height;
    double area() { return width * height; }
}

final class Triangle extends Shape {
    double base, height;
    double area() { return 0.5 * base * height; }
}

Только Circle, Rectangle и Triangle могут расширять Shape. Любой другой class, пытающийся расширить его, вызовет ошибку компиляции.

Каждый разрешённый подкласс должен использовать один из трёх модификаторов: final (дальнейшее расширение запрещено), sealed (цепочка ограничений продолжается) или non-sealed (открывает возможность неограниченного расширения):

sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // Не может быть расширен
non-sealed class Truck extends Vehicle { }    // Любой может расширить Truck

Sealed classes особенно мощны в сочетании с сопоставлением с образцом в выражениях switch, поскольку компилятор знает все возможные подтипы и может проверить полноту обработки. Они идеально подходят для моделирования фиксированного набора типов в предметной области, например способов оплаты, типов ответов или геометрических фигур.

challenge icon

Задание

Легко

Давайте создадим систему уведомлений, которая демонстрирует sealed classes, ограничивая типы уведомлений, которые могут существовать в вашем приложении. Вы создадите иерархию, в которой разрешены только определённые типы уведомлений, а каждый тип обрабатывает доставку по-своему.

Вы организуете код в четырёх файлах:

  • Notification.java: Создайте sealed abstract class, которая служит base для всех уведомлений. Она должна разрешать ровно три подкласса: EmailNotification, SMSNotification и PushNotification. Ваш sealed class должен содержать protected field message (String), constructor для его initialize, getter для message и abstract method deliver(), который возвращает String с описанием способа отправки уведомления.
  • EmailNotification.java: Создайте final class, которая extends Notification. EmailNotification содержит дополнительный private field recipient (String) для адреса электронной почты. Его constructor должен принимать и message, и recipient, вызывая parent constructor с помощью super(). Метод deliver() должен возвращать: Sending email to [recipient]: [message]
  • SMSNotification.java: Создайте non-sealed class, которая extends Notification. Это позволяет другим классам при необходимости extend её в future. SMSNotification содержит дополнительный private field phoneNumber (String). Его constructor принимает message и номер телефона. Метод deliver() должен возвращать: Sending SMS to [phoneNumber]: [message]
  • PushNotification.java: Создайте final class, которая extends Notification. PushNotification содержит дополнительный private field deviceId (String). Его constructor принимает message и идентификатор устройства. Метод deliver() должен возвращать: Sending push to device [deviceId]: [message]
  • Main.java: Объедините вашу систему уведомлений! Вы получите четыре inputs: message (String), адрес электронной почты (String), номер телефона (String) и идентификатор устройства (String).

    Создайте по одному объекту каждого типа уведомления, используя один и тот же message, но соответствующую контактную информацию. Сохраните все три объекта в array типа Notification[], чтобы продемонстрировать полиморфизм с помощью sealed classes. Затем Iterate через array и выведите результат вызова deliver() для каждого уведомления.

Вы получите четыре inputs в следующем порядке: message, адрес электронной почты, номер телефона и идентификатор устройства.

Обратите внимание, как sealed class ограничивает иерархию: только три разрешённых class могут extend Notification. Каждый подкласс должен объявить себя как final, sealed или non-sealed. Это даёт вам полный контроль над иерархией типов, сохраняя возможность использовать полиморфизм!

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

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Прочитать четыре входных значения
        String message = scanner.nextLine();
        String email = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        String deviceId = scanner.nextLine();
        
        // TODO: Создать по одному уведомлению каждого типа, используя одно и то же сообщение
        // - EmailNotification с message и email
        // - SMSNotification с message и phoneNumber
        // - PushNotification с message и deviceId
        
        // TODO: Сохранить все три уведомления в массив Notification[]
        // Это демонстрирует полиморфизм с sealed-классами
        
        // TODO: Пройти по массиву и вывести результат deliver()
        // для каждого уведомления
        
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Java