Java 17 中的密封类是什么?

60 java class sealed-class java-sealed-type java-17

今天,我将Java版本从16更新到17,我发现sealed类是其中的一个新功能。我认为可以这样声明:

public sealed class Main permits AClass, AnotherClass {
}
Run Code Online (Sandbox Code Playgroud)

但是,Java 中的密封类有什么用呢?

我还知道这是JDK 15中的预览功能。

JDT*_*One 47

您可以点击此链接查看示例。

简而言之,密封类使您可以控制哪些模型、类等可以实现或扩展该类/接口。

链接中的示例:

public sealed interface Service permits Car, Truck {

    int getMaxServiceIntervalInMonths();

    default int getMaxDistanceBetweenServicesInKilometers() {
        return 100000;
    }
}
Run Code Online (Sandbox Code Playgroud)

该接口只允许Car和Truck实现。

  • 有一个问题让我很困惑。为什么限制实现数量很有用。它会给编写单元测试带来麻烦,而且我没有看到真正的优势...... (17认同)
  • 我不明白这会让编写单元测试变得更加困难,请详细说明。使用密封类进行编写可以非常清楚类或接口的意图,并且可以帮助避免在有大量开发人员处理的大型代码存储库中发生一些不幸的事情。 (5认同)
  • @30thh Kotlin 中的一个常见模式是拥有一个“密封”“结果类”,它具有一组指示不同类型结果的子类,允许您通过“when”表达式对不同结果做出反应。我怀疑这也可能是 Java 中“密封”接口/类的一种用例。另一个可能的用例是拥有一个公共“密封”接口和一些“非密封”子接口,因为您的 API 设计要求您只实现子接口,而不是直接实现公共接口。这可能不是正确的 OOP,但这并不意味着某些 API 不会受益。 (3认同)

Pan*_*kos 33

JEP 409将其解释为

密封类或接口只能由那些允许这样做的类和接口进行扩展或实现。

更实际的解释如下:

过去的情况是:

  • 您无法限制一个接口被另一个接口扩展
  • 您无法限制哪些类能够实现特定接口。
  • 您必须将一个类声明为最终类,以免被另一个类扩展。这样,任何类都不能扩展声明的最终类。这是非黑即白的做法。

目前sealed关键字的情况是:

  • 现在,您可以限制其他接口扩展某个接口,并仅针对某些允许扩展该接口的特定接口制定规则。

    例子:

    public sealed interface MotherInterface permits ChildInterfacePermitted {}
    
    //Has to be declared either as sealed or non-sealed
    public non-sealed interface ChildInterfacePermitted extends MotherInterface {}  
    
    public interface AnotherChildInterface extends MotherInterface {} 
    //compiler error! It is not included in the permits of mother inteface
    
    Run Code Online (Sandbox Code Playgroud)
  • 您现在可以创建一个接口并仅选择允许实现该接口的特定类。所有其他类都不允许实现它。

    例子:

     public sealed interface MotherInterface permits ImplementationClass1 {} 
    
     //Has to be declared either as final or as sealed or as non-sealed
     public final class ImplementationClass1 implements MotherInterface {} 
    
     public class ImplementationClass2 implements MotherInterface {} 
     //compiler error! It is not included in the permits of mother inteface
    
    Run Code Online (Sandbox Code Playgroud)
  • 您现在可以限制要扩展的类(与之前的 Final 相同),但现在可以允许某些特定的类对其进行扩展。所以现在你有了更多的控制权,就像以前一样,关键字final绝对限制每个类扩展声明的final类

    例子:

    public sealed class MotherClass permits ChildClass1 {}
    
    //Has to be declared either as final or as sealed or as non-sealed
    public non-sealed class ChildClass1 extends MotherClass {} 
    
     public class ChildClass2 extends MotherClass {} 
     //compiler error! It is not included in the permits of MotherClass
    
    Run Code Online (Sandbox Code Playgroud)

重要笔记:

  • 密封类及其允许的子类必须属于同一模块,并且如果在未命名模块中声明,则必须属于同一包。

    例子:

    假设我们有相同的未命名模块和以下包

      -packageA
         -Implementationclass1.java
      -packageB
         -MotherClass.java
    
    Run Code Online (Sandbox Code Playgroud)

    或者

       -root
          -MotherClass.java
          -packageA
             -Implementationclass1.java
    
    Run Code Online (Sandbox Code Playgroud)

    您将收到错误Class is not allowed toextend sealed class from another package。因此,如果您有一个未命名的模块,则密封函数的所有参与类和接口都必须完全相同地放置在同一个包中。

  • 每个允许的子类必须直接扩展密封类。


Nik*_*las 6

密封课程

密封类是一种约束,只允许给定的类实现它。sealed这些允许的类必须显式扩展密封类,并且还具有、non-sealed或修饰符之一 final。该功能从 java 17 ( JEP 409 ) 开始提供,并且在更早之前 (Java 15) 就已作为预览版提供。

sealed interface IdentificationDocument permits IdCard, Passport, DrivingLicence { }
Run Code Online (Sandbox Code Playgroud)
final class IdCard implements IdentificationDocument { }
final class Passport implements IdentificationDocument { }
non-sealed class DrivingLicence implements IdentificationDocument { }
class InternationalDrivingPermit extends DrivingLicence {}
Run Code Online (Sandbox Code Playgroud)

与模式匹配一​​起使用

我发现这个新功能与 Java 17 ( JEP 406 )中作为预览引入的模式匹配结合起来非常棒!

允许的类限制确保所有子类在编译时都是已知的。使用switch表达式( Java 14 中的JEP 361),编译器需要列出所有允许的类或default对其余类使用关键字。考虑使用上面的类的以下示例:

final String code = switch(identificationDocument) {
    case IdCard idCard -> "I";
    case Passport passport -> "P";
};
Run Code Online (Sandbox Code Playgroud)

编译器会产生javac Application.java --enable-preview -source 17错误:

Application.java:9: error: the switch expression does not cover all possible input values
                final String code = switch(identificationDocument) {
                                    ^
Note: Application.java uses preview features of Java SE 17.
Note: Recompile with -Xlint:preview for details.
1 error
Run Code Online (Sandbox Code Playgroud)

一旦使用了所有允许的类或default关键字,编译就会成功:

final String code = switch(identificationDocument) {
    case IdCard idCard -> "I";
    case Passport passport -> "P";
    case DrivingLicence drivingLicence -> "D";
};
Run Code Online (Sandbox Code Playgroud)


rai*_*iks 5

密封类是 Java 语言的补充,为类作者提供了对哪些类可以扩展它的细粒度控制。以前,您可以允许每个人继承您的类,也可以完全禁止它(使用“final”)。它也适用于接口。

此外,它是模式匹配功能的先决条件,因为所有后代在编译期间都是已知的。

像往常一样,有一个缺点 - 密封类和接口不能被模拟/伪造,这是测试障碍。