Java Sealed Class (Java 17+)
Sealed class(封閉類別)允許限制哪些類別可以繼承或實作它,提供更精確的繼承控制。
基本語法
// 封閉類別,只允許指定的類別繼承
public sealed class Shape permits Circle, Rectangle, Triangle {
// 類別內容
}
// 必須是 final、sealed 或 non-sealed
public final class Circle extends Shape {
private double radius;
}
public sealed class Rectangle extends Shape permits Square {
// Rectangle 也是封閉的,只允許 Square 繼承
}
public final class Square extends Rectangle {
// final 類別不能被繼承
}
public non-sealed class Triangle extends Shape {
// non-sealed 開放任何類別繼承
}
permits 關鍵字
// 允許的子類別必須:
// 1. 在同一模組(或同一檔案如果是無模組專案)
// 2. 直接繼承封閉類別
// 3. 使用 final、sealed 或 non-sealed 修飾
public sealed class Animal permits Dog, Cat, Bird {
}
public final class Dog extends Animal {}
public final class Cat extends Animal {}
public sealed class Bird extends Animal permits Sparrow, Eagle {}
public final class Sparrow extends Bird {}
public final class Eagle extends Bird {}
封閉介面
public sealed interface Vehicle permits Car, Bike, Truck {
void start();
}
public final class Car implements Vehicle {
@Override
public void start() { System.out.println("汽車啟動"); }
}
public final class Bike implements Vehicle {
@Override
public void start() { System.out.println("機車啟動"); }
}
public non-sealed class Truck implements Vehicle {
@Override
public void start() { System.out.println("卡車啟動"); }
}
搭配 Record
public sealed interface Result<T> permits Success, Failure {
}
public record Success<T>(T value) implements Result<T> {}
public record Failure<T>(String error) implements Result<T> {}
// Record 預設是 final,所以符合 sealed 的要求
搭配 Pattern Matching
封閉類別和 pattern matching 搭配使用非常強大:
public sealed interface Shape permits Circle, Rectangle, Triangle {}
public record Circle(double radius) implements Shape {}
public record Rectangle(double width, double height) implements Shape {}
public record Triangle(double base, double height) implements Shape {}
public static double calculateArea(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> 0.5 * t.base() * t.height();
// 不需要 default,因為編譯器知道所有可能的子類型
};
}
同一檔案內的子類別
如果所有子類別都在同一個原始碼檔案中,可以省略 permits:
// Shape.java
public sealed class Shape {
// permits 自動推斷
}
final class Circle extends Shape {}
final class Rectangle extends Shape {}
final class Triangle extends Shape {}
反射 API
Shape shape = new Circle(5);
Class<?> clazz = Shape.class;
// 檢查是否為封閉類別
boolean isSealed = clazz.isSealed(); // true
// 取得允許的子類別
Class<?>[] permitted = clazz.getPermittedSubclasses();
for (Class<?> c : permitted) {
System.out.println(c.getName());
}
實用範例
狀態機
public sealed interface OrderState permits Pending, Processing, Shipped, Delivered, Cancelled {
}
public record Pending() implements OrderState {}
public record Processing(String processor) implements OrderState {}
public record Shipped(String trackingNumber) implements OrderState {}
public record Delivered(LocalDateTime deliveredAt) implements OrderState {}
public record Cancelled(String reason) implements OrderState {}
public class Order {
private OrderState state = new Pending();
public String getStatusMessage() {
return switch (state) {
case Pending p -> "等待處理";
case Processing p -> "處理中,處理人員: " + p.processor();
case Shipped s -> "已出貨,追蹤碼: " + s.trackingNumber();
case Delivered d -> "已送達於: " + d.deliveredAt();
case Cancelled c -> "已取消,原因: " + c.reason();
};
}
}
表達式樹
public sealed interface Expr permits Num, Add, Mul {
}
public record Num(int value) implements Expr {}
public record Add(Expr left, Expr right) implements Expr {}
public record Mul(Expr left, Expr right) implements Expr {}
public static int evaluate(Expr expr) {
return switch (expr) {
case Num n -> n.value();
case Add a -> evaluate(a.left()) + evaluate(a.right());
case Mul m -> evaluate(m.left()) * evaluate(m.right());
};
}
// 使用:(2 + 3) * 4
Expr expr = new Mul(new Add(new Num(2), new Num(3)), new Num(4));
System.out.println(evaluate(expr)); // 20
Sealed vs 其他方式
| 方式 | 用途 |
|---|---|
| final class | 完全禁止繼承 |
| package-private | 限制同套件可見 |
| sealed class | 精確控制允許的子類別 |
重點整理
sealed限制哪些類別可以繼承- 子類別必須使用
final、sealed或non-sealed permits列出允許的子類別- 同檔案的子類別可省略
permits - 搭配 pattern matching 可實現窮舉檢查
- 適合用於狀態機、領域模型等場景