GEP-13


元数据
编号

GEP-13

标题

密封类

版本

1

类型

功能

状态

草稿

负责人

Paul King

创建日期

2021-07-22

上次修改 

2021-07-22

摘要:密封类

密封类和接口限制了哪些其他类或接口可以扩展或实现它们。通过支持密封类和接口,Groovy 编程语言可以提供一种额外的机制来控制类层次结构的构建。

动机

继承是创建相关类和接口层次结构的强大机制。有时,希望限制此类层次结构中子项的定义。修饰符已经提供了一些机制

  • 如果我们所有的类和接口都是公开的,这表明我们希望最大程度地重用。

  • final 修饰符提供了一种在方法或类级别限制进一步继承的机制。它有效地限制了所有进一步的扩展,并表明不再需要代码重用。

  • 通过使基类包私有,我们可以将扩展限制为同一包中的类。如果一个抽象的 Shape 类是包私有的,我们可以在同一个包中拥有公共类 SquareCircle。这表明我们希望代码重用只发生在包内。虽然它确实限制了在原始包之外创建新的形状,但它没有为可以是正方形或圆形的形状提供抽象,因为 Shape 不是公开的。

  • 我们可以使用 protected 可见性来严格地限制成员对子类的访问,但这并不能帮助我们解决上面提到的问题,例如在讨论的示例中缺少对 Shape 的可见抽象。

密封类或接口可以是公开的,但有一个允许子类的关联列表。不在该列表中的类或接口不能从这些密封类型继承。这表明我们希望在层次结构内重用代码,但不在层次结构之外。层次结构中的父类可以被设为可访问的,而无需也使它们可扩展的。这允许创建层次结构,在层次结构内实现最大程度的重用,而无需在以后进行防御性编码以防添加任意扩展。

此类类在定义代数数据类型 (ADT) 中很有用,并且在我们需要推理是否已考虑所有可能的类型的情况下很有用,例如 静态编译器可能希望在 switch 块没有按各自的 case 分支详尽地覆盖所有可能的类型时发出警告。

初始实现

  • 提供一个 @Sealed 标记注释或 AST 变换,允许定义允许子类的列表。使用此注释将是一个孵化功能,可能会发生变化。最终可能不鼓励显式使用,而改为鼓励使用关键字,例如 sealed。但是,可以保留注释以在早期 JVM 或任何语法更改之前的 Groovy 版本上提供对此功能的支持。

  • 禁止扩展 JDK17+ 密封类或带注释的 @Sealed 类。这也适用于接口、匿名内部类和特性。

  • 在可能隐式发生这种扩展的其他地方提供检查,例如:使用 @Delegate,在使用类型强制转换时,等等。

  • 支持 non-sealedunsealed 子层次结构。(参见 JEP-409)

  • 仅在基类和所有允许的子类在同一文件中时,允许自动推断允许的子类。(参见 JEP-409)

  • 在语法中引入 sealed 修饰符和 permits 子句。

  • 默认情况下,在 JDK17+ 上运行时,密封类信息将添加到字节码中。我们将此类类称为本机密封类。

  • 默认情况下,在早期 JDK 上运行时,会将注释添加到类中以指示类是密封的。此类类将被 Groovy 4+ 编译器识别,但不会被 Java 识别。

  • @SealedOptions 注释有一个 mode 注释属性,可以覆盖默认行为。

潜在扩展

以下潜在扩展可能都令人想要,但对于第一个实现来说不是目标

  • 要求密封层次结构中的所有类在同一时间编译。

  • 要求密封层次结构中的所有类都属于同一个 JPMS 模块。

  • 如果对密封层次结构使用 switch,并且没有详尽地覆盖所有类型,则向静态编译器添加警告。

更新历史

1 (2021-07-22) 初始草稿 2 (2021-11-06) 更新以与 4.0.0-beta-2 保持一致