← Назад к вопросам

Что такое sealed class?

2.0 Middle🔥 51 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Sealed Class в Java

Sealed class (запечатанный класс) — это класс, который ограничивает, какие другие классы могут его расширять. Введён в Java 17 как preview feature, и стал обычной feature в Java 21.

Основная идея

Sealed class позволяет разработчику явно указать, какие классы могут наследоваться, что даёт больше контроля над иерархией наследования:

// До Java 17 — любой может расширить класс
public class Animal {
}

public class Dog extends Animal {}      // OK
public class Cat extends Animal {}      // OK
public class UnicornAnimal extends Animal {} // Тоже OK, но нежелательно!

// С Sealed class — контроль над наследованием
public sealed class Animal permits Dog, Cat {
}

public final class Dog extends Animal {}     // OK
public final class Cat extends Animal {}     // OK
public class UnicornAnimal extends Animal {} // Ошибка компиляции!

Синтаксис Sealed Class

1. Базовый синтаксис

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

// Разрешённые подклассы
public final class Circle extends Shape {
    private double radius;
    
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

public final class Rectangle extends Shape {
    private double width, height;
    
    @Override
    public double getArea() {
        return width * height;
    }
}

public final class Triangle extends Shape {
    private double a, b, c;
    
    @Override
    public double getArea() {
        // Формула Герона
        double s = (a + b + c) / 2;
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
    }
}

2. Не-final подклассы

Подкласс не обязан быть final, но может быть sealed сам:

public sealed class Vehicle permits Car, Truck, Bicycle {
}

// Может быть final
public final class Car extends Vehicle {
}

// Может быть sealed (промежуточный уровень)
public sealed class Truck extends Vehicle permits SportsTruck, DeliveryTruck {
}

public final class SportsTruck extends Truck {
}

public final class DeliveryTruck extends Truck {
}

// Может быть non-sealed
public non-sealed class Bicycle extends Vehicle {
    // Её можно расширять без ограничений
}

public class MountainBike extends Bicycle {} // OK

Для чего нужны Sealed Classes

1. Контроль над иерархией наследования

Запечатанные классы позволяют разработчику явно указать, какие расширения допустимы:

public sealed class PaymentMethod permits CreditCard, DebitCard, PayPal {
    public abstract void process(BigDecimal amount);
}

// Только эти три способа оплаты разрешены
public final class CreditCard extends PaymentMethod {
    @Override
    public void process(BigDecimal amount) { /* ... */ }
}

public final class DebitCard extends PaymentMethod {
    @Override
    public void process(BigDecimal amount) { /* ... */ }
}

public final class PayPal extends PaymentMethod {
    @Override
    public void process(BigDecimal amount) { /* ... */ }
}

// Это будет ошибкой компиляции
// public class CryptoCurrency extends PaymentMethod { }

2. Лучшая оптимизация компилятором

Компилятор знает все возможные подклассы, поэтому может сделать лучшие оптимизации:

public sealed class Status permits Active, Inactive, Pending {
}

// Компилятор знает, что это все возможные значения
Status status = getStatus();

if (status instanceof Active) {
    // Активирована работа
} else if (status instanceof Inactive) {
    // Деактивирована
} else if (status instanceof Pending) {
    // В ожидании
}
// Компилятор может предупредить, если забыли случай

3. Pattern Matching с Sealed Classes

Sealed classes отлично работают с pattern matching в switch:

public sealed interface HttpResponse permits SuccessResponse, ErrorResponse {}

public final class SuccessResponse implements HttpResponse {
    private int statusCode;
    private String body;
    
    public SuccessResponse(int statusCode, String body) {
        this.statusCode = statusCode;
        this.body = body;
    }
    
    public int getStatusCode() { return statusCode; }
    public String getBody() { return body; }
}

public final class ErrorResponse implements HttpResponse {
    private int statusCode;
    private String error;
    
    public ErrorResponse(int statusCode, String error) {
        this.statusCode = statusCode;
        this.error = error;
    }
    
    public int getStatusCode() { return statusCode; }
    public String getError() { return error; }
}

// Pattern matching в Java 21+
HttpResponse response = getResponse();
String result = switch (response) {
    case SuccessResponse(int code, String body) when code == 200 ->
        "Success: " + body;
    case SuccessResponse(int code, String body) ->
        "OK (" + code + "): " + body;
    case ErrorResponse(int code, String error) ->
        "Error (" + code + "): " + error;
};

4. Sealed Records

Sealed часто используется с records для создания алгебраических типов данных:

public sealed interface Result<T> permits Success, Failure {}

public record Success<T>(T value) implements Result<T> {}

public record Failure(String error) implements Result<Object> {}

// Использование
Result<Integer> result = calculateResult();

String message = switch (result) {
    case Success(var value) -> "Result: " + value;
    case Failure(var error) -> "Error: " + error;
};

5. Безопасность и документирование

Sealed class документирует намерение разработчика и предотвращает непредусмотренное расширение:

// Без sealed
public abstract class DatabaseConnection {
    public abstract void connect();
    public abstract void disconnect();
}
// Кто-то может создать DatabaseConnection и использовать неправильно

// С sealed
public sealed abstract class DatabaseConnection 
    permits PostgresConnection, MySqlConnection, OracleConnection {
    public abstract void connect();
    public abstract void disconnect();
}
// Ясно, что только эти три БД поддерживаются

Практический пример: HTTP API Response

public sealed interface ApiResponse permits SuccessApiResponse, ErrorApiResponse {
}

public record SuccessApiResponse<T>(
    int statusCode,
    T data,
    String message
) implements ApiResponse {}

public record ErrorApiResponse(
    int statusCode,
    String error,
    String message
) implements ApiResponse {}

@RestController
@RequestMapping("/api")
public class UserController {
    
    @GetMapping("/users/{id}")
    public ResponseEntity<?> getUser(@PathVariable Long id) {
        try {
            User user = userService.getUser(id)
                .orElseThrow(() -> new UserNotFoundException(id));
            
            return ResponseEntity.ok(
                new SuccessApiResponse<>(200, user, "User found")
            );
        } catch (UserNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
}

// Обработка ответа
ApiResponse response = getApiResponse();

String message = switch (response) {
    case SuccessApiResponse(int code, var data, var msg) ->
        msg + ": " + data;
    case ErrorApiResponse(int code, var error, var msg) ->
        error + " (" + code + ")";
};

Ограничения Sealed Classes

  1. Все разрешённые подклассы должны быть в той же пакете
  2. Подклассы должны быть явно указаны в permits
  3. Подклассы должны быть либо final, либо sealed, либо non-sealed
// Ошибка — подклассы в разных пакетах (в Java 17-20)
public sealed class Animal permits com.zoo.Dog { }
// В Java 21+ можно указать другие пакеты

Сравнение с final

// final — запрещает любое наследование
public final class Immutable {
}

// sealed — разрешает наследование только для конкретных классов
public sealed class Controlled permits AllowedSubclass {
}

Когда использовать Sealed Classes

  1. Явное ограничение иерархии наследования
  2. ADT (Algebraic Data Types) с records
  3. Работа с pattern matching
  4. API дизайн, где нужен контроль расширяемости
  5. Классификация типов (статусы, виды платежей и т.д.)

Резюме

Sealed class используется для:

  • Контроля над иерархией наследования
  • Документирования допустимых подклассов
  • Поддержки pattern matching
  • Лучшей оптимизации компилятором
  • Повышения безопасности и надёжности кода
  • Создания ADT с records
Что такое sealed class? | PrepBro