Java 10/11/17 新特性详解:var / Record / Sealed Class / Pattern Matching

引言

Java 11 到 Java 17 是一个 LTS(长期支持)到另一个 LTS 的演进区间,引入了多项改变编码习惯的重要特性。这些特性不是零散的语法糖,而是一整套组合拳,正在重塑 Java 的编码风格。


一、局部变量类型推断(var)

1.1 基本用法

Java 10 引入 var,用于局部变量的类型推断:

java 复制代码
// 编译器根据右侧推断类型
var name = "Alice";           // String
var age = 30;                 // int
var list = new ArrayList<String>(); // ArrayList<String>
var stream = list.stream();   // Stream<String>
var map = Map.of("a", 1, "b", 2); // Map<String, Integer>

1.2 适用范围

java 复制代码
// ✅ 局部变量
var list = new ArrayList<String>();

// ✅ for-each
for (var item : list) {
    System.out.println(item);
}

// ✅ for 循环
for (var i = 0; i < 10; i++) { ... }

// ✅ try-with-resources
try (var input = new FileInputStream("data.txt")) { ... }

// ❌ 方法参数
public void foo(var x) { } // 编译错误

// ❌ 返回类型
public var bar() { return 1; } // 编译错误

// ❌ 字段
private var name = "Alice"; // 编译错误

// ❌ 未初始化
var x; // 编译错误

// ❌ null 赋值
var x = null; // 编译错误,无法推断类型

1.3 最佳实践

java 复制代码
// ✅ 右侧类型明显时使用 var
var stream = users.stream().filter(u -> u.isActive()).map(User::getName);
var map = new ConcurrentHashMap<String, List<Order>>();
var path = Paths.get("/data/config.json");

// ❌ 右侧类型不明显时不要用 var
var result = process(data);    // process 返回什么?不清楚
var value = getData();         // getData 返回什么?不清楚

// ✅ 工厂方法返回类型清晰
var list = List.of("a", "b", "c");  // List<String>
var map = Map.of("k1", 1, "k2", 2); // Map<String, Integer>

// ✅ 简化泛型声明
// Before
Map<String, List<Map<String, Integer>>> data = new HashMap<>();
// After
var data = new HashMap<String, List<Map<String, Integer>>>();

二、Record(记录类)

2.1 基本用法

Java 14 预览,Java 16 正式发布。Record 是一种不可变数据载体:

java 复制代码
// 一行定义一个不可变数据类
public record Point(int x, int y) {}

// 等价于:
public final class Point extends Record {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int x() { return x; }
    public int y() { return y; }

    @Override
    public boolean equals(Object o) { ... }
    @Override
    public int hashCode() { ... }
    @Override
    public String toString() { return "Point[x=" + x + ", y=" + y + "]"; }
}

2.2 自定义构造器

java 复制代码
public record Range(int min, int max) {
    // 紧凑构造器:验证参数
    public Range {
        if (min > max) {
            throw new IllegalArgumentException("min > max");
        }
    }
}

// 带默认值的构造器
public record User(String name, int age, String role) {
    public User(String name, int age) {
        this(name, age, "user");
    }
}

2.3 添加方法

java 复制代码
public record Point(int x, int y) {
    // 实例方法
    public double distanceTo(Point other) {
        return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(y - other.y, 2));
    }

    // 静态工厂方法
    public static Point origin() {
        return new Point(0, 0);
    }
}

2.4 实现接口

java 复制代码
public record Point(int x, int y) implements Comparable<Point> {
    @Override
    public int compareTo(Point other) {
        int cmp = Integer.compare(x, other.x);
        return cmp != 0 ? cmp : Integer.compare(y, other.y);
    }
}

2.5 Record 与 JSON 序列化

java 复制代码
// Jackson 2.12+ 原生支持 Record
ObjectMapper mapper = new ObjectMapper();
Point point = mapper.readValue("{\"x\":1,\"y\":2}", Point.class);
String json = mapper.writeValueAsString(point);

2.6 Record 的限制

限制 说明
不可变 字段是 final 的
不能继承 隐式 final,不能 extends
不能被继承 不能作为父类
不能声明实例字段 只能有紧凑构造器中的逻辑
字段不能是可变的 不能有 setter

三、Sealed Class(密封类)

3.1 基本用法

Java 15 预览,Java 17 正式发布。密封类限制哪些类可以继承/实现它:

java 复制代码
// 密封接口:只允许三种形状
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 a, double b, double c) implements Shape {}

3.2 密封类规则

java 复制代码
// 子类必须在同一个模块/包中
// 子类必须是 final、sealed 或 non-sealed

sealed interface Shape permits Circle, Rectangle, Quadrilateral {}
    // Circle: final → 不能再被继承
    final record Circle(double radius) implements Shape {}

    // Rectangle: sealed → 可以继续限制子类
    sealed record Rectangle(double width, double height) implements Shape
        permits Square {}
        final record Square(double side) extends Rectangle(side, side) {}

    // Quadrilateral: non-sealed → 任何人都可以继承
    non-sealed class Quadrilateral implements Shape {}
        // 任何类都可以继承 Quadrilateral

3.3 密封类的价值

复制代码
没有密封类:
├── switch 必须加 default → 编译器不知道是否穷举
├── if-else 无法保证完整性 → 新增子类可能被遗漏
└── 类型层次是开放的 → 无法控制继承

有密封类:
├── switch 不需要 default → 编译器检查穷举
├── 新增子类必须修改 permits → 编译器提醒所有 switch
└── 类型层次是封闭的 → 可控的领域建模

四、Pattern Matching(模式匹配)

4.1 instanceof 模式匹配

Java 14 预览,Java 16 正式发布:

java 复制代码
// Before:先检查再转换
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// After:检查+绑定变量
if (obj instanceof String s) {
    System.out.println(s.length()); // 直接使用 s
}

// 带条件的使用
if (obj instanceof String s && s.length() > 5) {
    System.out.println(s.toUpperCase());
}

4.2 switch 模式匹配(Java 17 预览,Java 21 正式)

java 复制代码
// Before:繁琐的 if-else
static String format(Object obj) {
    if (obj instanceof Integer) {
        return "int: " + ((Integer) obj).intValue();
    } else if (obj instanceof Long) {
        return "long: " + ((Long) obj).longValue();
    } else if (obj instanceof String) {
        return "string: " + ((String) obj).length();
    } else {
        return "unknown";
    }
}

// After:switch 模式匹配
static String format(Object obj) {
    return switch (obj) {
        case Integer i -> "int: " + i;
        case Long l    -> "long: " + l;
        case String s  -> "string: " + s.length();
        case null      -> "null";
        default        -> "unknown";
    };
}

4.3 带守卫条件的 switch(Java 21)

java 复制代码
static String categorize(Object obj) {
    return switch (obj) {
        case String s when s.length() > 10 -> "long string: " + s;
        case String s                      -> "short string: " + s;
        case Integer i when i > 0          -> "positive: " + i;
        case Integer i                     -> "non-positive: " + i;
        default                            -> "other";
    };
}

4.4 Record 模式匹配(Java 21)

java 复制代码
// 解构 Record
record Point(int x, int y) {}

static void print(Object obj) {
    switch (obj) {
        case Point(int x, int y) -> System.out.println("Point(" + x + ", " + y + ")");
        default -> System.out.println("Other");
    }
}

// 嵌套解构
record Line(Point start, Point end) {}

static void printLine(Object obj) {
    switch (obj) {
        case Line(Point(int x1, int y1), Point(int x2, int y2)) ->
            System.out.printf("Line from (%d,%d) to (%d,%d)%n", x1, y1, x2, y2);
        default -> System.out.println("Other");
    }
}

4.5 密封类 + switch 模式匹配

java 复制代码
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double a, double b, double c) implements Shape {}

// 编译器知道所有子类,无需 default
static double area(Shape shape) {
    return switch (shape) {
        case Circle(var r)        -> Math.PI * r * r;
        case Rectangle(var w, var h) -> w * h;
        case Triangle(var a, var b, var c) -> {
            double s = (a + b + c) / 2;
            yield Math.sqrt(s * (s - a) * (s - b) * (s - c));
        }
    };
}

五、其他重要特性

5.1 文本块(Java 13 预览,Java 15 正式)

java 复制代码
// Before
String json = "{\n" +
    "  \"name\": \"Alice\",\n" +
    "  \"age\": 30\n" +
    "}";

// After
String json = """
    {
        "name": "Alice",
        "age": 30
    }
    """;

// SQL
String sql = """
    SELECT u.name, o.total
    FROM users u
    JOIN orders o ON u.id = o.user_id
    WHERE u.active = true
    """;

5.2 switch 表达式(Java 12 预览,Java 14 正式)

java 复制代码
// 语句 → 表达式,有返回值
String result = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> "relax";
    case TUESDAY                -> "work";
    case WEDNESDAY, THURSDAY    -> "busy";
    case SATURDAY               -> "play";
};

// yield 在块中返回值
int numLetters = switch (name) {
    case "Alice" -> 5;
    case "Bob" -> {
        System.out.println("Hi Bob!");
        yield 3;
    }
    default -> name.length();
};

5.3 Helpful NullPointerExceptions(Java 14)

java 复制代码
// Before
Exception in thread "main" java.lang.NullPointerException

// After:指出哪个变量为 null
Exception in thread "main" java.lang.NullPointerException:
    Cannot invoke "String.length()" because "user.name" is null

5.4 String 新方法

java 复制代码
// Java 11
"  hello  ".strip();        // "hello"(Unicode 感知)
"  hello  ".stripLeading(); // "hello  "
"  hello  ".stripTrailing();// "  hello"
"hello".repeat(3);          // "hellohellohello"
"hello".isBlank();          // false
"\n\n".lines().count();     // 0(空行不计)

// Java 12
"hello".indent(4);          // "    hello\n"
"    hello".stripIndent();  // "hello"

// Java 12
String formatted = "Name: %s, Age: %d".formatted("Alice", 30);

5.5 HttpClient(Java 11 正式)

java 复制代码
HttpClient client = HttpClient.newHttpClient();

// GET
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/data"))
    .header("Accept", "application/json")
    .GET()
    .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

// POST
HttpRequest postRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/data"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"Alice\"}"))
    .build();

// 异步
CompletableFuture<HttpResponse<String>> future =
    client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

5.6 Files 新方法

java 复制代码
// Java 11
Path path = Paths.get("data.txt");
String content = Files.readString(path);
Files.writeString(path, "hello world");

// 指定编码
String content = Files.readString(path, StandardCharsets.UTF_8);

六、特性组合实战

6.1 领域建模:密封类 + Record

java 复制代码
// 支付方式建模
sealed interface PaymentMethod permits CreditCard, DebitCard, BankTransfer, Cash {}
record CreditCard(String number, String expiry, String cvv) implements PaymentMethod {}
record DebitCard(String number, String bank) implements PaymentMethod {}
record BankTransfer(String account, String routingNumber) implements PaymentMethod {}
record Cash() implements PaymentMethod {}

// 处理逻辑:switch 模式匹配
String describe(PaymentMethod method) {
    return switch (method) {
        case CreditCard(var num, var exp, _) -> "Credit card ending in " + num.substring(num.length() - 4);
        case DebitCard(var num, var bank)    -> "Debit card from " + bank;
        case BankTransfer(var acct, var rt)  -> "Bank transfer from " + acct;
        case Cash                            -> "Cash payment";
    };
}

6.2 结果类型:密封类 + Record + var

java 复制代码
sealed interface Result<T> permits Success, Failure {}
record Success<T>(T value) implements Result<T> {}
record Failure<T>(String error) implements Result<T> {}

var result = fetchData();
var output = switch (result) {
    case Success(var data) -> "Got: " + data;
    case Failure(var err)  -> "Error: " + err;
};

总结

特性 Java 版本 核心价值
var 10 减少样板代码,局部变量类型推断
switch 表达式 14 switch 有返回值,箭头语法
文本块 15 多行字符串,告别拼接
Record 16 不可变数据载体,一行替代几十行
Sealed Class 17 封闭类型层次,可控继承
Pattern Matching for instanceof 16 消除强制转换
Pattern Matching for switch 21 类型匹配 + 守卫条件 + Record 解构

这些特性组合使用,正在让 Java 走向更声明式、更安全的编程风格。