sealed关键字是从jdk15开始预览,直到jdk17成为正式版,可以对继承父类和实现接口进行更加细粒度的限制,之前的限制也只有final用于禁止继承,默认包权限限制在同一个包内,sealed密封类/接口可以明确指定哪些类可以进行继承和实现;
跟sealed相关的关键字除了之前的final,还有新增的non-sealed和permits:
- permits:用于明确指定允许继承的子类和实现类,且不能使用匿名类动态类,书写位置在extends/implements后面(大概是放最后面);
- sealed:指定类/接口为密封类/接口,后面必须使用permits指定子类/实现类;
- final:指定类为最终类,不能被继承;
- non-sealed:解除密封性,指定为普通类关系;
sealed可以修饰类也可以修饰接口,密封类的子类必须使用sealed、final、non-sealed其中之一修饰,sealed修饰的子类继续密封,后面继续使用permits指定,final修饰的子类为最终类,不能被继承,non-sealed修饰的子类密封性解除,后续可以当普通类使用;
permits指定的允许类为直接指定,不具有传递性;
代码展示:
package test;
public class MainTest {
//密封类A只能被A1,A2,A3继承
public sealed class A permits A1, A2, A3 {}
//A1为final类,不能继续被继承
public final class A1 extends A {}
//A2使用non-sealed修饰,解除密封限制可被任何类继承
public non-sealed class A2 extends A {}
//A3仍为密封类,需要继续用permits指定可继承类
public sealed class A3 extends A permits B {}
public final class B extends A3 {}
// public final class A4 extends A {} //报错,A4未被A指定未可继承类
public class C extends A2 {} //任何类都可继承A2
public static void main(String[] args) {
}
}
另外,sealed类和子类/实现类,可以在同一个包下,也可以在不同的包下,但是必须在同一个模块下,如果使用未命名的模块,则必须在同一个包里;
需要在不同包里使用时需要建一个命名的模块:
另外,由于record类型最后编译成final类并且不支持继承和被继承,所以可以跟sealed接口一起使用,使代码更简介:
package test;
public class MainTest {
public sealed interface A permits B, C {
public void fun();
}
public record B(String s) implements A {
@Override
public void fun() {
System.out.println("B fun:" + s);
}
}
public record C(int i) implements A {
@Override
public void fun() {
System.out.println("C fun:" + i);
}
}
public static void main(String[] args) {
A b = new B("abc");
A c = new C(321);
b.fun();
c.fun();
}
}