# 问题
33. Sealed Class(密封类)如何限制子类继承?
# 标准答案
密封类(Sealed Class) 是 Java 15 引入的一项特性,它允许开发者限制一个类的继承范围,从而确保类的继承结构在设计时是可控的。这一特性使得开发者可以精确地控制哪些类可以继承自某个父类,而哪些类不能继承,从而提高代码的可维护性、可预测性和安全性。
# 1. 基本概念
- 密封类通过在类声明中使用
sealed
修饰符来定义。它规定了只能从指定的子类中继承该类。 - 子类继承密封类时,必须在类声明中使用
permits
关键字列出允许继承该类的子类。
例如:
public sealed class Shape permits Circle, Rectangle {
// 密封类的内容
}
public final class Circle extends Shape {
// Circle 类的内容
}
public final class Rectangle extends Shape {
// Rectangle 类的内容
}
// 试图继承密封类时会报错
// public class Triangle extends Shape { } // 编译错误
2
3
4
5
6
7
8
9
10
11
12
13
14
在上面的例子中,Shape
类被声明为密封类,并且只允许 Circle
和 Rectangle
类继承。如果你尝试定义其他的子类(例如 Triangle
),编译器会报错。
# 2. 如何限制子类继承
密封类通过 permits
关键字指定哪些类可以继承。只列举了在 permits
中的类才能继承密封类,其他类无法继承该密封类。
例如:
public sealed class Shape permits Circle, Rectangle, Square {
// Shape 的实现
}
public final class Circle extends Shape { /* 实现 */ }
public final class Rectangle extends Shape { /* 实现 */ }
public final class Square extends Shape { /* 实现 */ }
// 下面的类无法继承 Shape,编译时会报错
// public final class Triangle extends Shape { } // 编译错误
2
3
4
5
6
7
8
9
10
在这个例子中,只有 Circle
、Rectangle
和 Square
类被允许继承 Shape
类,其他任何类都无法继承 Shape
类。
# 3. 密封类的继承层级
- 密封类不仅限于直接继承它的子类,子类也可以是其他类的密封类,从而进一步限制继承层级。
- 密封类可以是 final(表示没有其他子类可以继承),也可以是 non-sealed(表示该类的子类可以自由继承该类)。
例如:
public sealed class Shape permits Circle, Rectangle {
// Shape 的实现
}
public non-sealed class Circle extends Shape {
// Circle 类可以继续被继承
}
public final class Rectangle extends Shape {
// Rectangle 不能被继承
}
public class Triangle extends Circle {
// Triangle 继承自 Circle
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在上面的例子中,Circle
是 non-sealed
类,意味着它可以被进一步继承,而 Rectangle
是 final
类,不能被继承。
# 4. 使用密封类的好处
- 控制继承结构:通过限制继承,密封类使得开发者能够更好地控制类的继承结构,避免无序的继承链。
- 类型安全:密封类可以在代码中保证类型安全。例如,使用
switch
语句时,可以确保仅处理已知的子类。 - 增强可维护性:密封类的继承结构更加明确和有序,使得代码更易于理解和维护。
- 性能优化:Java 编译器可以对密封类的继承层次进行优化,提高程序的性能,尤其是在模式匹配(如
switch
)中。
# 5. 密封类与接口的结合
密封类可以和接口一起使用。密封类不仅可以指定继承层次,还可以实现接口,从而进一步增强灵活性。
public sealed interface Shape permits Circle, Rectangle {
double area();
}
public final class Circle implements Shape {
// 实现方法
public double area() { return Math.PI * radius * radius; }
}
public final class Rectangle implements Shape {
// 实现方法
public double area() { return width * height; }
}
2
3
4
5
6
7
8
9
10
11
12
13
在这个例子中,Shape
是一个密封接口,只有 Circle
和 Rectangle
可以实现这个接口,其他类不能。
# 核心原理
密封类通过 sealed
修饰符和 permits
关键字来实现继承限制。在 Java 编译时,编译器会检查继承层次结构,确保只有允许的类才能继承密封类,而其他类会产生编译错误。
# 常见错误
- 忘记列出允许继承的类:如果没有在
permits
中列出所有允许继承的类,编译时会报错。 - 错误地继承密封类:如果尝试继承密封类,但没有在
permits
列表中指定允许继承的类,编译时会产生错误。
# 最佳实践
- 使用密封类限制继承层次,确保代码中只有已知的类继承某个类。
- 将密封类和
switch
语句结合使用,利用模式匹配来提高代码的可读性和安全性。
# 性能优化
密封类的继承结构经过编译时的优化,可以减少运行时的判断和类型转换,提高程序性能。特别是在模式匹配和 switch
语句中,密封类能够提供更加高效的执行路径。
# 深入追问
🔹 密封类如何与抽象类结合使用? 🔹 密封类能否与枚举结合使用? 🔹 密封类和传统类有什么主要区别?
# 相关面试题
- 什么是 Java 中的密封类?
- 密封类如何提升代码的可维护性和类型安全性?
- 如何使用密封类在设计中控制类的继承层次?