大家好,我是小面。今天将给大家介绍一下Java中的密封类。
Sealed类是Java武器库中的一个新引入(JDK17)。由于这一添加,在Java编程语言中的关键字集合中添加了另一个关键字。事实上,引入了几个新的关键字来支持密封类:密封和非密封。
这些类扩充了继承的经典概念,在继承概念中,只有一组特定的子类才能扩展父类。因此,本质上,Final类不允许继承。同时,非final类可以由任何子类继承。Sealed类介于两者之间,只指定允许继承的一些类。本编程教程通过代码示例和用例介绍了Java中Sealed类的用法和概念。
什么是Java中的继承?
作为一个补充,继承是一种面向对象的特性,其中继承的类扩展了其父类的特性或功能。这增强了可重用性。子类继承父类的质量(字段、方法和嵌套类),并可以通过多态性和函数重载添加自己的质量。与其他支持多重继承的面向对象编程语言不同,Java严格支持单一继承。一个子类只能扩展一个父类(父类和子类之间的父子关系)。下面是Java中的继承示例:
代码语言:javascript复制class Bird {
String greet;
Bird(){
greet="?!";
}
void saySomething(){
System.out.println(greet);
}
}
class Duck extends Bird{
Duck(){
greet="Quack, Quack";
}
void saySomething(){
System.out.println(greet);
}
}
然而,请注意,当开发人员使用接口时,没有这种限制。Java允许类实现多个接口,如以下示例代码所示:
代码语言:javascript复制class Bird {
String greet;
Bird(){
greet="?!";
}
void saySomething(){
System.out.println(greet);
}
}
interface CanFly{
default void shoo() {
System.out.println("fly...");
}
}
interface CannotFly{
default void shoo() {
System.out.println("run...");
}
}
class Ostrich extends Bird implements CannotFly{
Ostrich(){
greet="boom";
}
void saySomething(){
System.out.println(greet);
shoo();
}
}
什么是Java的final类?
如果程序员想要限制类的继承或使其绝对不可继承,我们可以简单地以最后一个关键字开始类定义。此关键字的目的是防止类被子类化。因此,类变得不可修改和不可扩展。下面是一些示例代码,演示如何在Java中使用final关键字:
代码语言:javascript复制final class A {
void func(){
System.out.println("final class");
}
}
class B extends A {} // 这里是不允许继承的
Java中的抽象类是什么?
如果开发人员希望确保在不扩展类的情况下无法创建任何对象,我们可以使用关键字abstract声明一个类。尽管抽象可以具有常规类的所有特性,但使用abstract关键字使其变得特别。为了创建这个类的对象,程序员需要用一个非抽象类来扩展它,只有这样我们才能创建它的实例。从这个意义上讲,接口实际上像Java中的纯抽象类。下面是一个示例:
代码语言:javascript复制abstract class Shape{
void show(){
System.out.print("Shape");
}
}
class Box extends Shape{}
//...
Shape s = new Shape(); // 这个会报错! 不能创建对象
Shape s = new Box(); // 这样是可以的
Java中的密封类是什么?
正如您所看到的,在引入Sealed类之前,继承在Java中是一种要么全有要么全无的事情。并没有中间的规定,这意味着——如果我们希望允许某些类继承其他类无法继承的限制,该怎么办。这种类型的限制性或选择性继承在Sealed类中是可能的。它实际上由两个极端类组成:Final类和Abstract类,前者完全阻止继承,后者强制继承。Sealed类使开发人员能够精确地指定允许哪些子类扩展超级类。
因为有密封类,所以也有密封接口。两者都能更好地控制继承。这在设计类库时特别有用。
程序员可以用关键字Sealed声明一个Sealed类。然后我们提供类名并使用permit子句指定允许的子类。注意,关键字sealed和permissions都是上下文敏感的,并且与类或接口声明相关具有特殊含义;在Java中,它们除了此之外没有任何意义。
Java中的密封类声明如下:
代码语言:javascript复制public sealed class A permits B, C {
//...
}
在这个代码示例中,类A可由类B和C继承——或允许继承;没有其他类可以继承它。
代码语言:javascript复制public final class B extends A { }
public final class C extends A { }
public final class D extends A { } // 这里会报错! D 不能继承 A
注意,允许的类被声明为final。这意味着允许的子类不能进一步继承。然而,除了final关键字之外,我们可以使用其他子句,例如非密封的或与子类密封的。换句话说,密封类的子类必须声明为final、sealed或非sealed。
现在,如果我们希望类B可以由类D进一步扩展,我们可以如下声明D:
代码语言:javascript复制public sealed class B extends A permits D {}
public final class D extends B {} // 喏,现在可以继承了
现在,假设我们希望类A由B和C扩展,我们也希望类D扩展类B,但我们不希望类D声明为final、非密封或密封,那么我们可以按如下方式设计类:
代码语言:javascript复制Public sealed class A permits B,C{}
public non-sealed class B extends A { }
public class D extends A { } // OK
密封类的一些关键要求包括:
- 允许的子类必须可由Sealed类访问。
- 密封类和子类必须位于同一个命名模块中,尽管它们可以位于不同的包中。
- 对于未命名的模块,密封类和子类必须在同一个包中。
Java中的密封接口
密封接口的声明方式与密封类几乎相同。这里,permit子句用于指定允许哪个类或接口实现或扩展Sealed接口。例如,我们可以如下声明一个Sealed接口(这里,Sealed接口允许a类和B类实现):
代码语言:javascript复制public sealed interface SealedInterface permits A, B {}
A class that implements a Sealed interface must be declared as final, sealed, or non-sealed:
代码语言:javascript复制public non-sealed class A implements SealedInterface{}
public final class B implements SealedInterface{}
如果接口扩展了密封接口,则必须将其声明为密封或非密封。例如:
代码语言:javascript复制public non-sealed interface AI extends SealedInterface{}
关于Java中密封类的最后思考
Java中Sealed类和接口的引入为Java编程语言的继承特性注入了一些灵活性。尽管它们可能在特殊情况下使用,但密封类可能在API库的类设计中。重点是语言中提供了灵活性;程序员可以根据程序的要求使用它们。这个特性最好的一点是它在继承的使用上带来了某种形式的灵活性,在这种情况下,必须在完全限制继承的Final类之间缩小,或者在必须完全继承的抽象类之间缩小。
今天的分享就到此结束,如果觉得本文不错的还请伙伴们帮忙点赞转发,欢迎持续关注我们!