簡體   English   中英

Java 17 中的密封類是什么?

[英]What are sealed classes in Java 17?

今天,我將我的 Java 版本從 16 更新到 17,我發現sealed類是其中的一個新特性。 我認為可以這樣聲明:

public sealed class Main permits AClass, AnotherClass {
}

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

我也知道這是 jdk-15 中的預覽功能。

您可以按照此鏈接查看示例。

簡而言之,密封類使您可以控制哪些模型、類等可以實現或擴展該類/接口。

來自鏈接的示例:

public sealed interface Service permits Car, Truck {

    int getMaxServiceIntervalInMonths();

    default int getMaxDistanceBetweenServicesInKilometers() {
    return 100000;
  }
}

該接口只允許 Car 和 Truck 實現。

JEP 409將其解釋為

密封類或接口只能由允許這樣做的類和接口進行擴展或實現。

更實際的解釋如下:

過去的情況是:

  • 你不能限制一個接口被另一個接口擴展
  • 您無法限制哪些類能夠實現特定接口。
  • 您必須將一個類聲明為 final 才能不被另一個類擴展。 這樣,任何類都不能擴展聲明的 final 類。 這是非黑即白的方法。

現在的情況是:

  • 您現在可以限制由其他接口擴展的接口,並為僅允許擴展它的某些特定接口制定規則。

    例子:

     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
  • 您現在可以創建一個接口並僅選擇允許實現該接口的特定類。 所有其他類都不允許實現它。

    例子:

     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
  • 您現在可以限制要擴展的類(與之前的 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

重要筆記:

  • 密封類及其允許的子類必須屬於同一個模塊,並且如果在未命名的模塊中聲明,則屬於同一個包。

    例子:

    假設我們有相同的未命名模塊和以下包

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

    或者

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

    您將收到錯誤Class is not allowed to extend seal class from another package 因此,如果您有一個未命名的模塊,則密封函數的所有參與類和接口必須完全放在同一個包中。

  • 每個允許的子類必須直接擴展密封類。

根據本文檔,密封類和接口限制了哪些其他類或接口可以擴展或實現它們。 它更像是一種限制超類使用的聲明方式,而不是使用訪問修飾符。

在 Java 中,一個類可以是 final 的,因此沒有其他類可以繼承它。 如果一個類不是最終的,那么它對所有其他類開放以支持代碼可重用性。 這樣做會引起數據建模問題。

下面的 NumberSystem 類對所有類開放,因此任何子類都可以擴展它。 如果您想將此 NumberSystem 限制為一組固定的子類(Binary、Decimal、Octal 和 HexaDecimal)怎么辦? 這意味着您不希望任何其他任意類擴展此 NumberSystem 類。

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

使用密封類,您可以通過控制可以擴展它的子類來實現它,並防止任何其他任意類這樣做。

所有密封的 java 類或接口都必須使用 permit 關鍵字,這里是示例。

父類

公共密封類父許可 Child1,Child2 {

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

}

Child1.java

公共最終類 Child1 擴展父 {

public static void main(String[] args) {

    Child1 obj = new Child1();

    obj.parentMethod();
}

}

Child2.java

公共最終類 Child2 擴展 Parent{

public static void main(String[] args) {

    Child2 obj = new Child2();

    obj.parentMethod();
}

}

Child3.java

公共最終類 Child3 擴展 Parent{

public static void main(String[] args) {

    Child3 obj = new Child3();

    obj.parentMethod();
}

}

此 Child 3 類代碼將引發編譯時錯誤,指出擴展密封類 Parent 的類型 Child3 應該是 Parent 的允許子類型(允許 Child3 像 child 1 和 child2)

密封類

密封的 class 是一個僅允許給定類實現它的約束。 這些允許的類必須明確擴展密封的 class 並且還具有sealednon-sealedfinal修飾符之一。 該功能從 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 {}

與模式匹配一起使用

從 Java 17 ( JEP 406 ) 開始,我發現這個新功能與作為預覽引入的模式匹配相結合非常棒!

允許的 class 限制確保所有子類在編譯時都是已知的。 使用switch表達式(從 Java 14 開始的JEP 361 )編譯器需要列出所有允許的類或對其余類使用default關鍵字。 考慮使用上述類的以下示例:

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

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

一旦使用了所有允許的類或default關鍵字,則編譯成功:

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

密封類

  • 密封類是限制其他類或接口可以擴展它的類或接口。
  • 密封類和接口表示受限制的類層次結構,提供對繼承的更多控制。
  • 密封類的所有直接子類在編譯時都是已知的。 編譯帶有密封類的模塊后,不得再出現其他子類。
  • 例如,第三方客戶無法在他們的代碼中擴展您的密封類。 因此,密封類的每個實例都有一個來自有限集合的類型,該集合在編譯此類時是已知的。

定義密封類

要密封一個類,在它的聲明中添加sealed 修飾符。 然后,在任何 extends 和 implements 子句之后,添加 permit 子句。 該子句指定了可以擴展密封類的類。

例如,以下 Shape 聲明指定了三個允許的子類,Circle、Square 和 Rectangle:

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

在與密封類相同的模塊或相同的包中定義以下三個允許的子類 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 還有一個子類,FilledRectangle:

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

對允許的子類的限制

  • 它們必須在編譯時可由密封類訪問。

    例如,要編譯Shape.java ,編譯器必須能夠訪問 Shape 的所有允許類: Circle.javaSquare.javaRectangle.java 此外,由於 Rectangle 是一個密封類,編譯器還需要訪問FilledRectangle.java

  • 他們必須直接擴展密封類。

  • 它們必須恰好具有以下修飾符之一來描述它如何繼續由其超類啟動的密封:

    1. final : 不能進一步擴展
    2. 密封:只能通過其允許的子類擴展
    3. 非密封:可以通過未知子類擴展; 密封類不能阻止其允許的子類這樣做
  • 例如,Shape 的允許子類演示了這三個修飾符中的每一個: Circle是最終的,而Rectangle是密封的,而 The Square是非密封的。

  • 它們必須與密封類位於同一個模塊中(如果密封類在命名模塊中)或在同一個包中(如果密封類在未命名模塊中,如 Shape.java 示例中所示)。

例如,com.example.graphics.Shape的以下聲明中,其permitted subclasses都在不同的包中。 僅當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