第 10 章 / 共 20 章
枚举与 record
enum 进阶、record 自动样板、sealed 类
基础 enum
enum 定义一组固定的常量值。每个枚举常量本质是该 enum 类型的一个静态实例,编译期就确定,类型安全(编译器能挡住非法值)、可以用在 switch 里。
// EnumBasic.java
public class EnumBasic {
enum Status { PENDING, ACTIVE, DONE, CANCELLED }
public static void main(String[] args) {
Status s = Status.ACTIVE;
System.out.println(s); // ACTIVE
System.out.println(s.name()); // ACTIVE
System.out.println(s.ordinal()); // 1(声明顺序)
// 遍历所有值
for (Status v : Status.values()) {
System.out.println(v);
}
// 从字符串还原(找不到抛 IllegalArgumentException)
Status parsed = Status.valueOf("DONE");
System.out.println(parsed);
}
}enum + switch 表达式
switch 表达式(Java 14+)与 enum 配合特别合适:编译器能检查穷举——所有 case 都列了,default 可省;缺哪个 case 就编译报错。
// EnumSwitch.java
public class EnumSwitch {
enum Color { RED, GREEN, BLUE }
static String hex(Color c) {
return switch (c) {
case RED -> "#ff0000";
case GREEN -> "#00ff00";
case BLUE -> "#0000ff";
};
}
public static void main(String[] args) {
for (Color c : Color.values()) {
System.out.println(c + " " + hex(c));
}
}
}带字段与方法的 enum
enum 也是类——可以有字段、构造器、方法。每个常量在声明时通过 NAME(参数) 调用构造器。常用于"带元数据的枚举"。
// HttpStatus.java
public class HttpStatus {
enum Code {
OK(200, "OK"),
NOT_FOUND(404, "Not Found"),
SERVER_ERROR(500, "Internal Server Error");
final int value;
final String reason;
Code(int value, String reason) {
this.value = value;
this.reason = reason;
}
boolean isError() {
return value >= 400;
}
}
public static void main(String[] args) {
for (Code c : Code.values()) {
System.out.printf("%d %s (error=%b)%n", c.value, c.reason, c.isError());
}
}
}enum 实现接口
enum 可以 implements 接口,让每个常量都有特定行为——很优雅的"策略模式"实现。
// EnumStrategy.java
public class EnumStrategy {
interface BinaryOp {
int apply(int a, int b);
}
enum Op implements BinaryOp {
ADD { @Override public int apply(int a, int b) { return a + b; } },
SUB { @Override public int apply(int a, int b) { return a - b; } },
MUL { @Override public int apply(int a, int b) { return a * b; } };
}
public static void main(String[] args) {
for (Op op : Op.values()) {
System.out.println(op + "(2,3) = " + op.apply(2, 3));
}
}
}record(Java 14+ 正式 Java 16)
record 是"不可变数据载体"的语法糖。一行声明就免费送你:private final 字段、规范构造器、访问器(不是 getX 而是 x())、equals、hashCode、toString。极大减少 DTO 类的样板代码。
// RecordBasic.java
public class RecordBasic {
record Point(int x, int y) {}
public static void main(String[] args) {
Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);
System.out.println(p1.x()); // 访问器是 x() 不是 getX()
System.out.println(p1); // Point[x=1, y=2] (自动 toString)
System.out.println(p1.equals(p2)); // true (自动 equals)
System.out.println(p1.hashCode() == p2.hashCode()); // true (自动 hashCode)
}
}record 紧凑构造器(验证)
record 默认构造器把参数赋给同名字段。如果要加校验,写一个"紧凑构造器"——没有参数列表,直接在 { } 里写校验代码。fields 会在最后自动赋值。
// RecordValidate.java
public class RecordValidate {
record Range(int low, int high) {
public Range { // 紧凑构造器
if (low > high) {
throw new IllegalArgumentException("low > high");
}
}
// 可以加额外方法
public boolean contains(int x) {
return x >= low && x <= high;
}
}
public static void main(String[] args) {
Range r = new Range(1, 10);
System.out.println(r.contains(5)); // true
try {
new Range(10, 1);
} catch (IllegalArgumentException e) {
System.out.println("bad: " + e.getMessage());
}
}
}sealed 类与接口(Java 17+)
sealed 限制"哪些类能继承我"。结合 record 与 switch 模式匹配,可以表达"代数数据类型"——这是 Kotlin / Rust 里常见的特性,Java 终于补上了。
// SealedDemo.java
public class SealedDemo {
sealed interface Shape permits Circle, Square, Triangle {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
record Triangle(double base, double height) implements Shape {}
static double area(Shape s) {
// Java 17 暂用 instanceof 模式;Java 21 起可在 switch 表达式直接 case Circle c ->
if (s instanceof Circle c) return Math.PI * c.r() * c.r();
else if (s instanceof Square sq) return sq.side() * sq.side();
else if (s instanceof Triangle t) return 0.5 * t.base() * t.height();
throw new IllegalStateException("unreachable");
}
public static void main(String[] args) {
Shape[] shapes = { new Circle(2), new Square(3), new Triangle(4, 5) };
for (Shape s : shapes) {
System.out.printf("%s -> %.2f%n", s, area(s));
}
}
}