今天我将我的 java 版本从 16 更新到 17,我发现sealed
class 是其中的一个新特性。我认为可以这样声明——
public sealed class Main{
}
但是java中密封类有什么用呢?
我也知道这是 jdk-15 中的预览功能
今天我将我的 java 版本从 16 更新到 17,我发现sealed
class 是其中的一个新特性。我认为可以这样声明——
public sealed class Main{
}
但是java中密封类有什么用呢?
我也知道这是 jdk-15 中的预览功能
简而言之,密封类让您可以控制可以实现或扩展该类/接口的模型、类等。
链接中的示例:
public sealed interface Service permits Car, Truck {
int getMaxServiceIntervalInMonths();
default int getMaxDistanceBetweenServicesInKilometers() {
return 100000;
}
}
这个接口只允许 Car 和 Truck 实现它。
JEP 409将其解释为
密封的类或接口只能由允许这样做的类和接口扩展或实现。
更实际的解释如下:
过去的情况是:
使用密封关键字的当前情况是:
您现在可以限制由其他接口扩展的接口,并为仅允许扩展它的某些特定接口制定规则。
例子:
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。因此,如果您有一个未命名的模块,则密封函数的所有参与类和接口必须完全放在同一个包中。
每个允许的子类都必须直接扩展密封类。
密封类
定义密封类
要密封类,请将密封修饰符添加到其声明中。然后,在任何 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.java
、
Square.java
和Rectangle.java
。此外,由于 Rectangle 是一个密封类,编译器还需要访问FilledRectangle.java
.
他们必须直接扩展密封类。
它们必须恰好具有以下修饰符之一来描述它如何继续由其超类发起的密封:
例如,允许的 Shape 子类演示了这三个修饰符中的每一个:Circle
is final whileRectangle
是 seal 和 TheSquare
是 non-sealed。
它们必须与密封类在同一个模块中(如果密封类在命名模块中)或在同一个包中(如果密封类在未命名模块中,如 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 { }
所有sealed
java 类或接口都必须使用permits
关键字。例如:
父类:
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
and Child2
) 的允许子类型。
根据本文档,密封类和接口限制了哪些其他类或接口可以扩展或实现它们。它更像是一种声明性方式来限制超类的使用,而不是使用访问修饰符。
在 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 { ... }
使用密封类,您可以通过控制可以扩展它的子类来实现它,并防止任何其他任意类这样做。