无法穷举

判断一个形状是不是正方形

image-20250803175034811

  1. 上述判断 - 一个形状的对象是不是一个正方形的实例
  2. 一个形状的对象即使不是一个正方形的实例,也可能是一个正方形
    • 很多形状的特殊形式就是正方形 - 长方形、菱形、梯形、多边形等 - 无法穷举
  3. 通过 instanceof 并不能正确判断一个形状是否为正方形

问题根源 - 无限制的扩展性

image-20250803180025703

限制扩展性

  1. OOP 的最佳实践之一,就是把可扩展性限制在可以预测控制的范围内,而不是无限的扩展性
  2. 继承安全缺陷
    • 一个可扩展的类,子类父类可能会相互影响,从而导致不可预知的行为
    • 涉及敏感信息的类,增加可扩展性不一定是个优先选项,要尽量避免父类或者子类的影响
  3. 在设计 API 时,需要反复思考
    • 一个,有没有真实的可扩展需求,能不能使用 final 修饰符
    • 一个方法,子类有没有重写的必要性,能不能使用 final 修饰符
  4. 限制不可预测可扩展性,是实现安全代码健壮代码的一个重要目标
  5. JDK 17 之前,限制可扩展性只有两个方法 - 使用私有类或者 final 修饰符
    • 私有类不是公开接口,只能内部使用,而 final 修饰符则彻底放弃了可扩展性
    • 要么全开放,要么全封闭,可扩展性只能在两个极端游走
    • 全封闭彻底没有了可扩展性,而全开放又面临固有的安全缺陷
  6. 在 JDK 17 后,使用 sealed 修饰的类就是封闭类,使用 sealed 修饰的接口就是封闭接口
    • 封闭类和封闭接口限制可以扩展实现他们的其它类或接口
  7. 通过把可扩展性限制放在可以预测和控制的范围内,封闭类和封闭接口打开了全封闭全开放两个极端之间的中间地带

声明封闭类

  1. 类型分类
    • 被扩展的父类 - 封闭类
    • 扩展而来的子类 - 许可类
  2. 封闭类的声明使用 sealed 类修饰符,然后在所有的 extendsimplements 语句之后,使用 permits 指定允许扩展该封闭类的子类

Sealed class permits clause must contain all subclasses

image-20250803223017727

image-20250803223109732

All sealed class subclasses must either be final, sealed or non-sealed

image-20250803223220232

  1. permits 关键字指定许可子类(permitted subclasses)必须和封闭类处在同一个模块(module)或者包空间(package)里
  2. 如果封闭类许可类是在同一个模块里,那么他们可以处在不同的包空间
  3. 如果许可子类封闭类同一个源代码文件里,封闭类可以不使用 permits 语句
    • Java 编译器检索源文件,在编译期封闭类添加上许可子类

声明许可类

许可类的声明需要满足以下三个条件

  1. 许可类必须和封闭类处于同一个模块(module)或者包空间(package)里,即在编译时,封闭类必须可以访问它的许可类
  2. 许可类必须是封闭类直接扩展类
  3. 许可类必须声明是否继续保持封闭 - All sealed class subclasses must either be final, sealed or non-sealed
Key Value Desc
final 终极类 关闭扩展性
sealed 封闭类 延续受限制的扩展性
non-sealed 解封类 支持不受限制的扩展性

Sealed class must have subclasses

image-20250803225351599

  1. 许可类必须是封闭类直接扩展,因此许可类不具备传递性
  2. ColoredCircle 是 Circle 的子类,但 Circle 是解封类,不是 Shape 封闭类的直接扩展,因此 ColoredCircle 不是 Shape 的许可类

案例回顾

如何判断一个形状是不是正方形

  1. 将形状类定义为封闭类
    • Sealed class permits clause must contain all subclasses
    • 此时,所有形状的子类是可以穷举
    • 然后需找可以用来表示正方形的许可类
  2. Shape 是一个封闭类,本质上一个扩展性受限的类,因此我们能穷举所有扩展性

优先级

可扩展性的限定方法 - 优先级由高到低

  1. 使用私有类
  2. 使用 final 修饰符
  3. 使用 sealed 修饰符
  4. 不受限制的扩展性 - 不推荐 - 失控