密封类是Java 17正式支持的一个新特性,它让Java中类的继承可以更加细粒度的进行控制。今天就来认识一下这个新的功能。 密封类 在以往的Java类继承中,Java类的继承控制非常有限,仅能通过 final 关键字和访问控制符来控制类的继承。例如final 类无法被集成;包私有类仅仅只能在该包下进行继承。 这显然是不够的。如果一个功能只允许出现在 Phone 和Pad 上,不允许出现在Computer 上。如果不对该功能的继承实现进行限制,开发人员将很容易滥用该功能的实现类,错误地重用一些代码。这就是密封类产生的原因。 密封类的声明❝ 密封类不仅仅可以是类,也可以是接口。文章中的密封类为统称 密封类(接口)可以明确哪些类和接口可以对其扩展或实现。你可以通过 sealed 修饰符来表明某个类是密封类。但是下面是一个错误的密封类声明: /** * 这是一个错误的示范 */ public sealed interface SealedService { void doSomething(); } 密封类(接口)在声明的时候必须明确可继承(实现)的范围,所以上面的写法是错误的。必须用 permits 子句指定允许扩展密封类的类,而且permits 关键字位于extends 或者implements 之后。 ❝ 简而言之,密封类明确了哪些其他类(或接口)可以扩展它们。 下面是正确的写法: /** * 这是一个正确的示范,明确了可继承的子类为{@link SealedServiceImpl} * 该密封类接口同时实现了{@link SuperService} */ public sealed interface SealedService extends SuperService permits SealedServiceImpl { void doSomething(); } /** * 密封类子类 */ public final class SealedServiceImpl implements SealedService { @Override public void doSomething() { System.out.println("这是一个密封类子类"); } } 密封类子类的类型 在上面示例中,密封类(接口)的实现类用了 final 关键字标记,当然密封类的实现类还可以是密封类: /** * 密封类子类 */ public sealed class SealedServiceImpl implements SealedService permits SonService { @Override public void doSomething() { System.out.println("这是一个密封类子类"); } } public final class SonService extends SealedServiceImpl { } 那么难道密封类(接口)的子类只能是 final 类或者密封类,就不能再扩展了?答案是否定的,只需要使用关键字non-sealed 显式声明密封类的继承实现为非密封类就可以继续扩展了。 public non-sealed class SealedServiceImpl implements SealedService { @Override public void doSomething() { } /** * 用{@code non-sealed}声明非密封类,就可以继续扩展了 */ static class NonSealedExtend extends SealedServiceImpl { } } 总结一下,密封类的子类要么是 final Class ;要么是 sealed Class ;要么是 non-sealed Class 。 permits 声明的类必须是直接子类 密封类 permits 关键字声明的子类必须是直接实现类,为了证明这一点我们这样写: /** * 错误的示范 */ public sealed interface SealedService extends SuperService permits SealedServiceImpl, SonService { void doSomething(); } public sealed class SealedServiceImpl implements SealedService permits SonService { @Override public void doSomething() { System.out.println("这是一个密封类子类"); } } public final class SonService extends SealedServiceImpl { } 我使用 SonService 间接实现了SealedService ,结果报错,报错信息要求必须是直接的继承关系。 错误的密封类继承实现 从上图可以看出 SonService 并非直接实现SealedService ,这样会打破密封类的规则,所以无法编译通过。 ❝ 密封类中 permits 关键字声明的子类必须是直接子类,不可间接实现。 密封类不支持匿名类和函数式接口 由于密封类必须明确继承实现关系,所以它不支持匿名类。 /** * 密封类无法使用匿名类 * * @return the sealed service */ public SealedService sealedService(){ // 提示 Anonymous classes must not extend sealed classes return new SealedService() { @Override public void doSomething() { } }; } 同样也不支持函数式接口: /** * 错误的示范 */ @FunctionalInterface public sealed interface SealedService permits SealedServiceImpl { void doSomething(); } 总结 密封类已经在Java 17中正式转正,这也是Java 17的非常重要的特性之一。对于需要细粒度控制继承关系的场景来说是非常有用的。 原文链接:https://mp.weixin.qq.com/s/9R8G9PJrPzFlED9MHzKm8A 原作者:码农小胖哥