Java 17新特性解析:Records与Sealed Classes如何重塑现代代码设计

Java语言在不断地演进中,旨在提升开发效率、增强代码表现力并强化类型系统。Java 17作为最新的长期支持(LTS)版本,引入了多项重要特性,其中Records(记录类)和Sealed Classes(密封类)尤为引人注目。它们不仅仅是语法糖,更是从语言层面重塑Java面向对象编程和领域建模方式的强大工具,共同协作以创建更简洁、更安全、意图更明确的领域模型。

Records:透明数据载体

Records的主要目标是提供一种简洁的语法来声明主要用于存储数据的不可变类。在Records出现之前,创建一个纯粹的数据载体类需要编写大量样板代码,如私有final字段、全参构造器、getter方法、equals()、hashCode()和toString()方法。Records通过一行简单的声明自动实现了所有这些。

一个典型的Record声明如下所示:

public record Person(String name, int age) {}

这简洁的一行代码等价于一个具有两个final字段(name和age)、一个规范构造器、以及自动实现的访问器方法(name()和age())、equals()、hashCode()和toString()方法的完整类。Records的透明性特性意味着其状态(即组件列表)是API的一部分,这使得它们非常适合作为数据传输对象(DTO)、值对象或代数数据类型的变体。

Sealed Classes:受控的类层次结构

Sealed Classes(密封类)允许类和接口明确指定哪些其他类或接口可以扩展或实现它们。这为类的层次结构提供了严格的继承控制,是定义代数数据类型(ADT)的关键工具。通过使用sealed、permits和非密封(non-sealed)等关键字,开发者可以精确约束类型的可扩展性。

一个密封接口及其实现类的示例:

public sealed interface Shape permits Circle, Rectangle, Triangle {    double area();}public final class Circle implements Shape {    private final double radius;    // 实现 area() 方法}public final class Rectangle implements Shape {    private final double width, height;    // 实现 area() 方法}public non-sealed class Triangle implements Shape {    private final double base, height;    // 实现 area() 方法}

在这个例子中,Shape接口被声明为sealed,并明确许可(permits)只有Circle、Rectangle和Triangle三个类可以实现它。其中,Circle和Rectangle被标记为final,禁止进一步扩展,而Triangle被标记为non-sealed,允许被其他类继承。这种设计使得编译器能够在编译时检查模式匹配的穷尽性,增强代码的安全性。

协同效应:构建类型安全的领域模型

Records和Sealed Classes的结合使用,为构建类型安全、表达力强的领域模型提供了强大的组合工具。Records作为纯粹、不可变的数据载体,而Sealed Classes定义了有限且已知的子类型集合,二者协同工作可以实现功能完备且安全的代数数据类型。

考虑一个表示表达式的领域模型:

public sealed interface Expr permits Constant, Plus, Times {    int eval();}public record Constant(int value) implements Expr {    public int eval() { return value; }}public record Plus(Expr left, Expr right) implements Expr {    public int eval() { return left.eval() + right.eval(); }}public record Times(Expr left, Expr right) implements Expr {    public int eval() { return left.eval()  right.eval(); }}

这个模型利用Sealed Interface Expr限制了表达式的可能类型(常量、加法、乘法),并使用Records来简洁地定义每种类型的不可变数据结构和行为。结合Java 17中增强的模式匹配(如switch表达式),可以轻松编写出安全且易于理解的业务逻辑:

static String format(Expr e) {    return switch (e) {        case Constant(var value) -> String.valueOf(value);        case Plus(var left, var right) -> format(left) +  +  + format(right);        case Times(var left, var right) -> ( + format(left) +    + format(right) + );        // 无需default分支,因为所有情况均已覆盖    };}

编译器能够验证这个switch表达式是否穷尽了所有可能的Expr子类型,如果未来新增一种表达式类型(如Minus),编译器会立即报错,提示开发者需要处理新的情况,从而在编译期就避免了运行时错误。

对现代代码设计的深远影响

Records和Sealed Classes的引入,标志着Java向更具表现力和安全性的函数式与数据导向编程风格迈出了重要一步。它们促使开发者更多地思考数据的不变性、类型的封闭性以及行为的组合方式,而非传统的基于可变性和继承的面向对象设计。

这种转变带来的好处是多方面的:代码更加简洁,减少了样板代码;领域模型的意图更加清晰,通过类型系统本身表达了业务约束;模式匹配的结合使用使得代码更加健壮,减少了潜在的错误;不可变数据模型天然支持并发编程,减少了同步的复杂性。这些特性共同作用,使得Java开发者能够构建出更易于理解、维护和演进的现代应用程序。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐