public sealed interface Service permits Car, Truck {
int getMaxServiceIntervalInMonths();
default int getMaxDistanceBetweenServicesInKilometers() {
return 100000;
}
}
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一样),但是你现在可以允许一些特定的类扩展它。
范例:
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
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 { ... }
enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY;
}
boolean isWeekday(Day day) {
switch (day) {
case SUNDAY:
case SATURDAY:
return true;
default:
return false;
}
字符串 而不是这样:
boolean isWeekday(int day) {
switch (day) {
case 0:
case 6:
return true;
case 1:
case 2:
case 3:
case 4:
case 5:
return false;
default:
throw new IllegalArgumentException("day must be between 0 and 6");
}
sealed interface UpsertUserResponse
permits UserCreated, UserUpdated, InvalidEmail, Unauthorized {
}
record UserCreated(UUID id) implements UpsertUserResponse {}
record UserUpdated(String oldEmail) implements UpsertUserResponse {}
record InvalidEmail(String reason) implements UpsertUserResponse {}
record Unauthorized implements UpsertUserResponse {}
String getResponseMessage(UpsertUserResponse response) {
return switch (shape) {
case UserCreated id -> "New user created with id " + id.toString();
case UserUpdated oldEmail -> "Email updated from previous value of " + oldEmail;
case InvalidEmail reason -> "The email you entered was invalid because " + reason;
case Unauthorized -> "You can't do that!"
}
}
9条答案
按热度按时间gg0vcinb1#
You can follow this link for examples.
简而言之,密封类使您可以控制哪些模型,类等可以实现或扩展该类/接口。
链接的例子:
字符串
这个接口只允许Car和Truck实现它。
rseugnpd2#
JEP 409解释为
密封的类或接口只能由那些被允许这样做的类和接口来扩展或实现。
更实际的解释如下:
范例:
字符串
范例:
型
范例:
型
* 重要提示:*
范例:
假设我们有相同的未命名模块和以下包
型
或
型
你会得到错误 Class is not allowed to extend sealed class from another package.所以如果你有一个未命名的模块,所有参与的类和sealed函数的接口必须完全放在同一个包上。
iih3973s3#
封闭类
密封类是一种约束,只允许给定的类实现它。这些被允许的类必须显式地扩展密封类,并且还具有
sealed
,non-sealed
或final
修饰符之一。该功能自Java 17(JEP 409)起提供,并且在更早的时候(Java 15)作为预览版提供。个字符
使用模式匹配
我发现这个新特性与Java 17(JEP 406)的预览版中引入的模式匹配结合起来非常棒!
允许的类限制确保所有子类在编译时都是已知的。使用
switch
表达式(Java 14的JEP 361),编译器需要列出所有允许的类,或者对剩余的类使用default
关键字。考虑以下使用上述类的示例:型
编译器在
javac Application.java --enable-preview -source 17
上会导致错误:型
一旦使用了所有允许的类或
default
关键字,编译就成功了:型
vohkndzv4#
密封类是Java语言的一个附加功能,它使类作者能够细粒度地控制哪些类可以扩展它。以前,你可以允许所有人继承你的类,也可以完全禁止继承(使用“final”)。它也适用于接口。
此外,它是模式匹配特性的先决条件,因为在编译期间所有的后代都是已知的。
像往常一样,有一个缺点-密封的类和接口不能被模仿/伪造,这是一个测试障碍。
6tr1vspr5#
根据这个documentation,密封类和接口限制了其他类或接口可以扩展或实现它们。它更像是一种声明性的方式来限制超类的使用,而不是使用访问修饰符。
在Java中,一个类可以是final的,所以没有其他类可以继承它。如果一个类不是final的,那么它对所有其他类都是开放的,以支持代码的可重用性。这样做会引起数据建模的关注。
下面的NumberSystem类对所有类开放,所以任何子类都可以扩展它。如果你想将这个NumberSystem限制为一组固定的子类(Binary,Decimal,Octal和HexaDecimal)呢?。这意味着你不希望任何其他任意类扩展这个NumberSystem类。
字符串
使用密封类,您可以通过控制可以扩展它的子类来实现它,并防止任何其他任意类这样做。
u7up0aaq6#
Java中什么是密封类?
final
修饰符可以被认为是一种强密封形式,其中扩展/实现被完全禁止。概念上:
final
=sealed
+空permits
子句。与
final
完全禁止扩展/实现不同,Sealed
class
/interface
限制其他类或接口可以扩展或实现它们。历史记录
1.密封类由JEP 360提出,并在JDK 15中作为预览功能提供。
1.这个JEP建议在JDK 17中完成密封类,与JDK 16没有任何变化。
目标
说明
1.通过将
sealed
修饰符应用于类/接口的声明,可以密封该类/接口。1.然后,在任何extends和implements子句之后,
permits
子句指定允许扩展密封类的类。示例
例如,下面的
Loan
声明指定了允许的UnsecuredLoan
、SecuredLoan
子类:字符串
使用模式匹配密封类的好处
使用模式匹配,而不是用 if-else 链检查密封类的示例,我们可以使用一个用类型测试模式增强的
switch
。这将允许Java编译器检查所有允许的类都为我们覆盖。
例如,考虑以下代码:
型
**Java编译器无法确保instanceof测试覆盖了Loan的所有允许子类。**因此,如果省略了任何instanceof Loan,则不会发出编译时错误消息。
相反,使用模式匹配
switch
表达式,编译器可以确认Loan的每个允许的子类都被覆盖了。此外,如果缺少任何case,编译器将发出错误消息:型
参考:JEP 360: Sealed Classes
polkgigr7#
所有
sealed
java类或接口必须使用permits
关键字。例如:Parent.class:
字符串
Child1.java:
型
Child2.java:
型
Child3.java
型
这个
Child3
类代码将抛出一个编译时错误,说扩展密封类Parent的类型Child3应该是Parent的允许子类型(permits Child3
,就像Child1
和Child2
一样)。jqjz2hbq8#
封印职业:密封类是一种允许开发人员控制其他类可以从它们继承的程度的功能。通过将类声明为密封,您可以指定允许哪些其他类继承它。此功能增强了封装并提供了对类层次结构和扩展的更多控制。https://java-speed.blogspot.com/2023/07/what-is-sealed-classes-in-java-17.html
fsi0uk1n9#
其他的答案都有标题问题(“Java 17中的密封类是什么?”)但是你在正文中的问题(“密封类的用途是什么?”)还没有真正得到解决。
密封类/接口是创建tagged union的一种方式。标记联合对于类就像Java枚举对于对象一样。
Java枚举允许您将类可以示例化的可能对象限制为一组特定的值。这有助于您像这样建模一周中的几天:
字符串
而不是这样:
型
如果你有一个枚举类型,那么你知道你有每一个可能的有效值。你可以保证你的switch语句穷尽地处理所有输入。
枚举的局限性在于它们只适用于单个类的对象。每个枚举值必须具有相同的示例变量,相同的方法和相同的构造函数。每个枚举值都是同一个类的单个对象。
密封的类/接口克服了这个限制。由于每个子类都是自己的类,所以你可以改变任何你可以在类中改变的东西,例如示例变量,方法,构造函数,额外的实现接口。下面是一个例子:
型
你知道你的case语句已经处理了所有可能的情况。你可以灵活地支持不同的用户id和电子邮件验证错误,而不必求助于可能处于不可能状态的可空变量。(例如,没有办法错误地设置用户id和验证错误。)
如何在测试中模拟出密封类/接口的子类?你不会。密封类/接口与普通数据聚合(如记录)很好地配对。你不会为枚举编写模拟,也不会为密封类编写模拟。