密封类是 Java 17
正式推出的新特性。密封类使用 sealed
关键字定义,用于限制和指定某个类的子类,从而明确哪些子类可以继承该类。与 final
类不同,后者无法派生出任何子类,密封类允许有限的特定子类进行派生。这一特性可以减少代码中不必要的复杂性,例如在进行类型判断时,可以减少需要处理的分支数量。
密封类语法:使用 sealed
关键字来声明密封类,并在类体中使用 permits
子句列出允许继承的子类。
public sealed class Father permits Son1, Son2 {}
需求引入:假设我们要设计一个自定义的 JSON
标准抽象库,将 JSONValue
定义为顶层抽象类。考虑到 JSON
值通常包括数组、数值、字符串、布尔值、对象和 null
,我们可以从 JSONValue
抽象类派生出具体子类,例如 JSONArray
。为了防止其他开发人员在这些具体子类上继续继承,进而破坏现有的层次结构,我们可以将 JSONArray
等子类设计为 final
类。但是,在 Java 17
之前,JSONValue
仍然可能被其他不相关的类继承。而在 Java 17
之后,我们可以使用 sealed
关键字来限制可继承的子类,从而更好地控制类的扩展性。
public abstract sealed class JSONValue permits JSONArray, JSONNumber, JSONString, JSONBoolean, JSONObject, JSONNull {}
特性优势:利用密封类的特性,我们可以更灵活地使用它们。例如,可以将 JSONValue
类与带模式匹配的 switch
表达式结合使用,代码如下所示。在这个代码中,省略了 default
分支,但是编译器能够检查出不需要 default
子句,因为 JSONValue
的所有直接子类都已经在 case
分支中列出。
public String type () {
return switch (this) {
case JSONArray j -> "array";
case JSONNumber j -> "number";
case JSONString j -> "string";
case JSONBoolean j -> "boolean";
case JSONObject j -> "object";
case JSONNull j -> "null";
// No default needed here
}
}
注意:在使用密封类的过程中,必须指明密封类的子类是
sealed
、final
还是允许继续派生子类。对于最后一种情况,必须将子类声明为non-sealed
。另外,在声明密封类时可以不加permits
子句,这样一来,这个类的所有直接子类都必须在同一个文件中声明,不能访问这个文件的程序员就不能派生它的子类。