sealed
类。我认为可以这样声明:public sealed class Main permits AClass, AnotherClass {
}
但是,在Java中封闭类有什么用呢?
我也知道这是JDK 15中的一个预览功能。
sealed
类。我认为可以这样声明:public sealed class Main permits AClass, AnotherClass {
}
简而言之,密封类使您能够控制哪些模型、类等可以实现或扩展该类/接口。
来自链接的示例:
public sealed interface Service permits Car, Truck {
int getMaxServiceIntervalInMonths();
default int getMaxDistanceBetweenServicesInKilometers() {
return 100000;
}
}
这个接口只允许 Car 和 Truck 实现它。
sealed
"result class",它具有一组子类,表示不同类型的结果,允许您通过 when
表达式对不同的结果做出反应。我认为这也可以是 Java 中 sealed
接口/类的一个用例。另一个可能的用例是拥有一个通用的 sealed
接口和几个 non-sealed
子接口,因为您的 API 设计要求您只实现子接口,而不是直接实现通用接口。这可能不是正确的 OOP,但这并不意味着某些 API 不会受益。 - SlawJEP 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
现在可以创建一个接口并选择只允许特定的类来实现该接口,其它所有类都不能实现它。
示例: 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 是绝对限制每个类都无法继承已声明为 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 sealed class from another package。所以,如果你有一个未命名模块,所有参与封闭函数的类和接口都必须放置在同一个包中。
每个允许的子类必须直接扩展密封类。
密封类
密封类是一种限制,只允许给定的类来实现它。这些允许的类必须明确地扩展密封类,并且还必须有以下其中之一的修饰符:sealed
,non-sealed
或final
。该功能已在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)一起使用非常棒!
允许的类限制确保所有子类在编译时都是已知的。使用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";
};
class NumberSystem { ... }
final class Binary extends NumberSystem { ... }
final class Decimal extends NumberSystem { ... }
final class Octal extends NumberSystem { ... }
final class HexaDecimal extends NumberSystem { ... }
permits
关键字进行封装。例如:
Parent.class:
public sealed class Parent permits Child1, Child2 {
void parentMethod() {
System.out.println("from a sealed parent class ");
}
}
Child1.java:
public final class Child1 extends Parent {
public static void main(String[] args) {
Child1 obj = new Child1();
obj.parentMethod();
}
}
Child2.java:
public final class Child2 extends Parent {
public static void main(String[] args) {
Child2 obj = new Child2();
obj.parentMethod();
}
}
Child3.java
public final class Child3 extends Parent {
public static void main(String[] args) {
Child3 obj = new Child3();
obj.parentMethod();
}
}
这个 Child3
类的代码会抛出一个编译时错误,说
扩展密封类 Parent 的类型 Child3 应该是 Parent 的允许子类型 (permits Child3
,就像 Child1
和 Child2
一样)。
permits
关键字的密封类编译时不会出现问题,因此似乎实际上并不需要它。从我的测试中,看起来当省略 permits
关键字时,只有嵌套类才被允许扩展密封类。(我认为这也适用于接口,但我还没有测试过) - Michael Pfafffinal
修饰符可以被视为一种强制封闭的形式,其中完全禁止扩展/实现。
从概念上讲:final
= sealed
+ 一个空的permits
子句。
与完全禁止扩展/实现的final
不同,Sealed
class
/interface
限制了哪些其他类或接口可以扩展或实现它们。
允许类或接口的作者控制负责实现它的代码。
提供比访问修饰符更具声明性的方式来限制超类的使用。
sealed
修饰符来封闭。permits
子句指定了允许扩展封闭类的类。Loan
声明指定了允许的UnsecuredLoan
和SecuredLoan
子类:
sealed interface Loan permits UnsecuredLoan,SecuredLoan{}
final class UnsecuredLoan implements Loan {}
record SecuredLoan() implements Loan{}
使用模式匹配,而不是使用if-else链来检查封闭类的实例,我们可以使用增强的switch
语句来进行类型测试匹配。
这将允许Java编译器为我们检查所有允许的类是否都被覆盖。
例如,考虑以下代码:
void checkLoanType(Loan loan) {
if (loan instanceof UnsecuredLoan unsecuredLoan) {
// something
} else if (loan instanceof SecuredLoan securedLoan) {
// something
}
}
void checkLoanType(Loan loan) {
switch (loan) {
case SecuredLoan securedLoan -> {} //generated by compiler.
case UnsecuredLoan unsecuredLoan -> {} //generated by compiler.
}
}