V
Vel·ToolKit
简洁 · 高效 · 即开即用
ZH
第 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));
        }
    }
}