简体   繁体   English

Java 17 中的密封类是什么?

[英]What are sealed classes in Java 17?

Today, I updated my Java version from 16 to 17, and I found that sealed classes is a new feature in it.今天,我将我的 Java 版本从 16 更新到 17,我发现sealed类是其中的一个新特性。 I think it can be declared like this:我认为可以这样声明:

public sealed class Main permits AClass, AnotherClass {
}

But, what is the use of sealed classes in Java?但是,Java 中的密封类有什么用?

I also knew that it was a preview feature in jdk-15.我也知道这是 jdk-15 中的预览功能。

You can follow this link for examples.您可以按照此链接查看示例。

In short sealed classes gives you the control of which models, classes etc. that can implement or extend that class/interface.简而言之,密封类使您可以控制哪些模型、类等可以实现或扩展该类/接口。

Example from the link:来自链接的示例:

public sealed interface Service permits Car, Truck {

    int getMaxServiceIntervalInMonths();

    default int getMaxDistanceBetweenServicesInKilometers() {
    return 100000;
  }
}

This interface only permits Car and Truck to implement it.该接口只允许 Car 和 Truck 实现。

The JEP 409 explains it as JEP 409将其解释为

A sealed class or interface can be extended or implemented only by those classes and interfaces permitted to do so.密封类或接口只能由允许这样做的类和接口进行扩展或实现。

A more practical explanation is the following:更实际的解释如下:

The situation in the past was:过去的情况是:

  • You could not restrict an interface being extended by another interface你不能限制一个接口被另一个接口扩展
  • You could not constraint which classes where able to implement a specific interface.您无法限制哪些类能够实现特定接口。
  • You had to declare a class as final in order to not be extended by another class.您必须将一个类声明为 final 才能不被另一个类扩展。 This way no class could extend the declared final class.这样,任何类都不能扩展声明的 final 类。 This was black or white approach.这是非黑即白的方法。

The situation now is:现在的情况是:

  • You can now restrict an interface being extended by other interfaces and make a rule for only some specific interfaces which will be allowed to extend it.您现在可以限制由其他接口扩展的接口,并为仅允许扩展它的某些特定接口制定规则。

    Example:例子:

     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
  • You can now create an interface and select only specific classes that are allowed to implement that interface.您现在可以创建一个接口并仅选择允许实现该接口的特定类。 All other classes are not allowed to implement it.所有其他类都不允许实现它。

    Example:例子:

     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
  • You can now restrict a class being extended (same as before with final) but you can now allow some specific classes to extend it.您现在可以限制要扩展的类(与之前的 final 相同),但您现在可以允许某些特定类扩展它。 So now you have more control as before the keyword final was absolute restricting every class from extending the declared final class所以现在你有更多的控制权,因为关键字 final 绝对限制每个类扩展声明的 final 类

    Example:例子:

     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

Important notes:重要笔记:

  • The sealed class and its permitted subclasses must belong to the same module, and, if declared in an unnamed module, to the same package.密封类及其允许的子类必须属于同一个模块,并且如果在未命名的模块中声明,则属于同一个包。

    Example:例子:

    Let's say that we have the same unnamed module and the following packages假设我们有相同的未命名模块和以下包

     -packageA -Implementationclass1.java -packageB -MotherClass.java

    or或者

     -root -MotherClass.java -packageA -Implementationclass1.java

    You will get the error Class is not allowed to extend sealed class from another package .您将收到错误Class is not allowed to extend seal class from another package So if you have an unnamed module all participating classes and interfaces for the sealed function must be placed exactly on the same package.因此,如果您有一个未命名的模块,则密封函数的所有参与类和接口必须完全放在同一个包中。

  • Every permitted subclass must directly extend the sealed class.每个允许的子类必须直接扩展密封类。

As per this documentation , Sealed classes and interfaces restrict which other classes or interfaces may extend or implement them.根据本文档,密封类和接口限制了哪些其他类或接口可以扩展或实现它们。 It is more of a declarative way to restrict the use of a superclass rather than using access modifiers.它更像是一种限制超类使用的声明方式,而不是使用访问修饰符。

In Java, a class can be final so no other classes can subclass it.在 Java 中,一个类可以是 final 的,因此没有其他类可以继承它。 If a class is not final, then it is open to all other classes to support code reusability.如果一个类不是最终的,那么它对所有其他类开放以支持代码可重用性。 Doing so would raise data modeling concerns.这样做会引起数据建模问题。

The below NumberSystem class is open to all classes, so any subclass can extend it.下面的 NumberSystem 类对所有类开放,因此任何子类都可以扩展它。 What if you want to restrict this NumberSystem to a fixed set of subclasses (Binary, Decimal, Octal, and HexaDecimal)?.如果您想将此 NumberSystem 限制为一组固定的子类(Binary、Decimal、Octal 和 HexaDecimal)怎么办? It means you don't want any other arbitrary class to extend this NumberSystem class.这意味着您不希望任何其他任意类扩展此 NumberSystem 类。

class NumberSystem { ... }
final class Binary extends NumberSystem { ... }
final class Decimal extends NumberSystem { ... }
final class Octal extends NumberSystem { ... }
final class HexaDecimal extends NumberSystem { ... }

Using sealed class, you can achieve it by controlling the subclasses that can extend it and prevent any other arbitrary class from doing so.使用密封类,您可以通过控制可以扩展它的子类来实现它,并防止任何其他任意类这样做。

All sealed java classes or interfaces must use permits keyword here is the example.所有密封的 java 类或接口都必须使用 permit 关键字,这里是示例。

Parent.class父类

public sealed class Parent permits Child1, Child2 {公共密封类父许可 Child1,Child2 {

void parentMethod() {
    System.out.println("from a sealed parent class ");
}

} }

Child1.java Child1.java

public final class Child1 extends Parent {公共最终类 Child1 扩展父 {

public static void main(String[] args) {

    Child1 obj = new Child1();

    obj.parentMethod();
}

} }

Child2.java Child2.java

public final class Child2 extends Parent{公共最终类 Child2 扩展 Parent{

public static void main(String[] args) {

    Child2 obj = new Child2();

    obj.parentMethod();
}

} }

Child3.java Child3.java

public final class Child3 extends Parent{公共最终类 Child3 扩展 Parent{

public static void main(String[] args) {

    Child3 obj = new Child3();

    obj.parentMethod();
}

} }

This Child 3 class code will throw a Compile time error saying The type Child3 extending a sealed class Parent should be a permitted subtype of Parent (permits Child3 like child 1 and child2)此 Child 3 类代码将引发编译时错误,指出扩展密封类 Parent 的类型 Child3 应该是 Parent 的允许子类型(允许 Child3 像 child 1 和 child2)

Sealed classes密封类

A sealed class is a constraint that permits only given classes to implement it.密封的 class 是一个仅允许给定类实现它的约束。 These permitted classes must explicitly extend the sealed class and also have one of the sealed , non-sealed , or final modifiers.这些允许的类必须明确扩展密封的 class 并且还具有sealednon-sealedfinal修饰符之一。 The feature is delivered as of java 17 ( JEP 409 ) and was available as a preview longer before (Java 15).该功能从 java 17 ( JEP 409 ) 开始提供,并且在更早的时间 (Java 15) 之前作为预览版提供。

sealed interface IdentificationDocument permits IdCard, Passport, DrivingLicence { }
final class IdCard implements IdentificationDocument { }
final class Passport implements IdentificationDocument { }
non-sealed class DrivingLicence implements IdentificationDocument { }
class InternationalDrivingPermit extends DrivingLicence {}

Usage with pattern matching与模式匹配一起使用

I find this new feature awesome in conjunction with pattern matching introduced as a preview as of Java 17 ( JEP 406 )!从 Java 17 ( JEP 406 ) 开始,我发现这个新功能与作为预览引入的模式匹配相结合非常棒!

The permitted class restriction assures all the subclasses are known in compile time.允许的 class 限制确保所有子类在编译时都是已知的。 Using the switch expressions ( JEP 361 as of Java 14) the compiler requires either to list all the permitted classes or use the default keyword for the remaining ones.使用switch表达式(从 Java 14 开始的JEP 361 )编译器需要列出所有允许的类或对其余类使用default关键字。 Consider the following example using the classes above:考虑使用上述类的以下示例:

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

The compiler upon javac Application.java --enable-preview -source 17 results in an error: 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

Once all permitted classes or the default keyword are used, the compilation is successful:一旦使用了所有允许的类或default关键字,则编译成功:

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

Sealed Classes密封类

  • A sealed class is a class or interface which restricts which other classes or interfaces may extend it.密封类是限制其他类或接口可以扩展它的类或接口。
  • Sealed classes and interfaces represent restricted class hierarchies that provide more control over an inheritance.密封类和接口表示受限制的类层次结构,提供对继承的更多控制。
  • All direct subclasses of a sealed class are known at compile time.密封类的所有直接子类在编译时都是已知的。 No other subclasses may appear after a module with the sealed class is compiled.编译带有密封类的模块后,不得再出现其他子类。
  • For example, third-party clients can't extend your sealed class in their code.例如,第三方客户无法在他们的代码中扩展您的密封类。 Thus, each instance of a sealed class has a type from a limited set that is known when this class is compiled.因此,密封类的每个实例都有一个来自有限集合的类型,该集合在编译此类时是已知的。

Defining Sealed Classes定义密封类

To seal a class, add the sealed modifier to its declaration.要密封一个类,在它的声明中添加sealed 修饰符。 Then, after any extends and implements clauses, add the permits clause.然后,在任何 extends 和 implements 子句之后,添加 permit 子句。 This clause specifies the classes that may extend the sealed class.该子句指定了可以扩展密封类的类。

For example , the following declaration of Shape specifies three permitted subclasses, Circle, Square, and Rectangle:例如,以下 Shape 声明指定了三个允许的子类,Circle、Square 和 Rectangle:

public sealed class Shape
    permits Circle, Square, Rectangle {
}

Define the following three permitted subclasses, Circle, Square, and Rectangle, in the same module or in the same package as the sealed class:在与密封类相同的模块或相同的包中定义以下三个允许的子类 Circle、Square 和 Rectangle:

public final class Circle extends Shape {
    public float radius;
}

public non-sealed class Square extends Shape {
   public double side;
}   

public sealed class Rectangle extends Shape permits FilledRectangle {
    public double length, width;
}

Rectangle has a further subclass, FilledRectangle: Rectangle 还有一个子类,FilledRectangle:

public final class FilledRectangle extends Rectangle {
    public int red, green, blue;
}

Constraints on Permitted Subclasses对允许的子类的限制

  • They must be accessible by the sealed class at compile time.它们必须在编译时可由密封类访问。

    For example, to compile Shape.java , the compiler must be able to access all of the permitted classes of Shape: Circle.java , Square.java , and Rectangle.java .例如,要编译Shape.java ,编译器必须能够访问 Shape 的所有允许类: Circle.javaSquare.javaRectangle.java In addition, because Rectangle is a sealed class, the compiler also needs access to FilledRectangle.java .此外,由于 Rectangle 是一个密封类,编译器还需要访问FilledRectangle.java

  • They must directly extend the sealed class.他们必须直接扩展密封类。

  • They must have exactly one of the following modifiers to describe how it continues the sealing initiated by its superclass:它们必须恰好具有以下修饰符之一来描述它如何继续由其超类启动的密封:

    1. final : Cannot be extended further final : 不能进一步扩展
    2. sealed : Can only be extended by its permitted subclasses密封:只能通过其允许的子类扩展
    3. non-sealed : Can be extended by unknown subclasses;非密封:可以通过未知子类扩展; a sealed class cannot prevent its permitted subclasses from doing this密封类不能阻止其允许的子类这样做
  • For example , the permitted subclasses of Shape demonstrate each of these three modifiers: Circle is final while Rectangle is sealed and The Square is non-sealed.例如,Shape 的允许子类演示了这三个修饰符中的每一个: Circle是最终的,而Rectangle是密封的,而 The Square是非密封的。

  • They must be in the same module as the sealed class (if the sealed class is in a named module) or in the same package (if the sealed class is in the unnamed module, as in the Shape.java example).它们必须与密封类位于同一个模块中(如果密封类在命名模块中)或在同一个包中(如果密封类在未命名模块中,如 Shape.java 示例中所示)。

For example, in the following declaration of com.example.graphics.Shape , its permitted subclasses are all in different packages.例如,com.example.graphics.Shape的以下声明中,其permitted subclasses都在不同的包中。 This example will compile only if Shape and all of its permitted subclasses are in the same-named module.仅当Shape及其所有允许的子类都在同名模块中时,才会编译此示例。

package com.example.graphics;

    public sealed class Shape 
        permits com.example.polar.Circle,
                com.example.quad.Rectangle,
                com.example.quad.simple.Square { }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM