java17新特性-密封类

java17新特性-密封类
jwangjava15-密封的类和接口(预览)
在Java 15中,密封类(Sealed Classes)是一种新的特性,用于限制类的继承。通过密封类,开发者可以明确指定哪些类可以继承自该类,从而提供了一种更精细的控制继承机制的方式。
密封类的定义非常简单,只需在类名之前使用sealed关键字即可将类声明为密封类。密封类隐式地是一个无法实例化的抽象类,其子类数量有限,并且这些子类在声明密封类时通过permits关键字明确指定。密封类的主要目的是在编译时限制类型集,确保类型安全
- 密封类具有以下特性和与别的类的不同之处
- 特定数量的子类:密封类是一个有特定数量子类的类,看上去和枚举(enum)有点类似。不过,在枚举中,每个类型只有一个对象(实例),而在密封类中,同一个类可以拥有几个对象。
- when表达式涵盖所有情况:由于密封类的子类将自身类型作为一种情况,因此密封类中的when表达式可以涵盖所有情况,从而避免使用else子句。
- 简化类型转换和条件判断:密封类允许在instanceof操作符中使用类型模式,从而简化类型转换和条件判断的代码
通过密封类,开发者可以更好地控制类的继承关系,防止不希望的类继承自特定的类,从而增强代码的可维护性和安全性。同时,密封类也为类型安全和模式匹配提供了更好的支持
在Java 15之前,如果要让一个类不能被继承和修改,通常使用final关键字对类进行修饰。然而,这种方式比较不灵活,因为它直接阻止了类的继承和修改。相比之下,密封类提供了一种更加灵活和精细的控制机制。
在Java中,用于限制类继承的主要特性主要有两个:final关键字和密封类(Sealed Classes)
final关键字
如果一个类被声明为final,那么它不能被继承。这意味着没有其他类可以扩展(即继承)这个类。使用final关键字可以阻止其他开发者创建该类的子类,这有助于确保类的行为不被意外地修改
final class MyClass { |
密封类(Sealed Classes)
从Java 15开始引入的密封类特性提供了一种更细粒度的控制继承的方法。密封类允许开发者明确指定哪些类可以继承它,而不是完全禁止继承。通过在密封类声明中使用permits关键字,可以指定允许继承该类的子类列表
sealed class MySealedClass permits SubClass1, SubClass2 { |
在这个例子中,只有SubClass1和SubClass2可以继承MySealedClass,任何其他尝试继承MySealedClass的类都会导致编译错误
需要注意的是,Java中的继承机制是单继承的,即一个类只能直接继承自一个父类。这种设计有助于简化类的层次结构,减少潜在的复杂性。然而,如果需要实现类似多重继承的功能,可以使用接口(interface)来达成。一个类可以实现多个接口,从而获取多个接口中定义的方法,而不需要直接继承多个类
java16-密封的类和接口(第二次预览)
Java 15 引入了密封类作为预览功能,它提供了对继承的细粒度控制。 Java16 提供了一些小的增强功能,并将此功能保留为预览版。 以下是密封类要考虑的要点 −
密封类使用 sealed 关键字声明。
密封类允许使用 permits 关键字声明哪个类可以是子类型。
扩展密封类的类必须声明为 sealed、non-sealed 或 final。
密封类有助于在继承中创建有限且可确定的类层次结构。
java17新特性-密封类(正式特性)
解决的核心问题
在传统 Java 继承模型中,开发者面临一个二元选择困境:
- 完全开放:使用普通类(无修饰符),允许任何类继承它。
- 完全封闭:使用 final 类,禁止任何类继承它。
- 这种二元模型存在显著缺陷:
- 过度开放问题:当类设计者希望只允许特定类继承时(如领域模型中的有限子类型),无法阻止其他无关类继承。
- 模式匹配缺陷:编译器无法确定类的完整继承体系,导致模式匹配时无法进行穷尽性检查。
- 领域建模局限:无法精确表达”此类型只能有 A、B、C 三种具体实现”的领域约束。
典型案例:图形系统中的 Shape 类型,设计者希望只允许 Circle 和 Rectangle 继承,但传统机制无法阻止其他开发者创建 Triangle 子类。
工作原理与核心机制
密封类通过三个关键元素实现精细化控制
- sealed 修饰符:声明类/接口为密封类型,限制继承关系
public sealed class Shape { ... } |
- permits 子句:明确指定允许继承的有限子类列表
public sealed class Shape permits Circle, Rectangle { ... } |
- 子类继承约束:被许可的子类必须使用以下修饰符之一
final:禁止进一步继承(叶子节点)
public final class Circle extends Shape { ... } |
sealed:允许继承但必须再次指定子类(中间节点)
public sealed class Rectangle extends Shape permits Square { ... } |
non-sealed:开放继承(回归传统模式)
public non-sealed class Rectangle extends Shape { ... } |
使用详解与最佳实践
- 基础结构
// 密封类声明 |
- 示例
// 密封类:交通工具 |
验证规则
- 子类可见性: permits 列表中的所有子类必须可访问。
- 直接继承: 只有 permits 列表中的类才能直接继承。
- 修饰符约束: 子类必须有 final、sealed 或 non-sealed 修饰符。
- 包/模块约束:
- 同一模块:子类需与密封类同包或显式导出。
- 不同模块:子类必须在显式导出的包中。
重要限制与注意事项
- 包可见性要求:所有子类必须与密封类在同一模块中。若在未命名模块中,则需在同一包中。
- 反射限制:通过反射创建未许可子类将抛出 InaccessibleObjectException。
- 序列化兼容性:实现 Serializable 时需确保所有许可子类保持兼容。
- 记录类(Record)支持:记录类可声明为密封类型:
public sealed interface Result<T> |


