为什么要使用密封类(总结)

本文介绍了密封类(sealed)的概念及其在.NET等平台中的应用。密封类主要用于防止类的继承,有助于保护知识产权、提高代码安全性及可预测性,并在一定程度上优化了性能。此外,文章还探讨了使用密封类的最佳实践。

1.    主要是为了不想被继承而设计的。在class前面用sealed修饰,代表私有的。

2.     保护知识产权

3.    密封类的优点
    1.版本控制: 如果类最开始就是密封的, 那么将来可以在不破坏兼容性的前提下改为非密封类
    2.性能有一定的提升: 如果调用密封类的虚方法, 编译器可以生成更高效的代码
    3.安全性和可预测性

    缺点:密封类限制了类的派生, 对开发人员造成不便

4.    当你设计一个类,并且在设计的时候就确定不希望将来有人(或者你自己)因为凌乱的继承关系,使这个类的最初动机变得扑朔迷离的时候,可以将这个类sealed(为什么要不希望自己继承这个类后做改动?因为你的脑袋并不是电脑,你的记性并不是那么好,最初的设计初衷,在以后系统越来越庞大的时候,你可能自己都忘了自己当初设计的这个类可能考虑到的某些很细微的因素,这个因素要求你将来不要改变这个类的成员或者任何属性),当你有个基类A,然后有个类B继承A,然后类C又继承B,类D又继承类C的时候,你可能会发生凌乱。这个时候,有些类在设计之初你就可以将它sealed,以确保将来在某一个时刻脑袋短路的情况想把这个类继承出一个子类来改写的时候,编译器就会报错提示你,“唤起”你的记忆。
比如.net中,你可能没注意到过,struct也是sealed的,你不能继承一个struct。另外,.net framework中还有一些成员都是静态类型的类,比如Pen和Brushes也是sealed的,因为画笔和画刷在设计之初都已经定义好了它都有哪些颜色,并且不希望将来改变画笔和画笔的这些属性,所以直接给它sealed掉。

另外,如果你的程序中有用到反射的话,CLR对用了sealed关键字的类和属性会比普通的类有一点性能上的提升。因为反射需要找回某个类的属性,没有继承关系的类,它的“关系网”不复杂,CLR能够很轻易地定位它的“位置”。

sealed还可以用于限制类成员方法等。
比如你有一个类A包含了一个vitual虚方法FOO1,另外一个类B继承了A,并且还有另外一个类C继承了类B,在这种情况下,类C可以跳过类B而直接从类A override 方法 FOO1,而不管类B是否override了方法FOO1,这个时候很容易发生凌乱。你的设计结构很有可能拎不清了。

什么时候用sealed关键字?事实上,所有的类在设计之初,你都可以设计为sealed,直到你将来在设计别的类的时候,确定需要从这个类有继承关系,因为任何类sealed以后,你还可以将它unsealed(直接以掉sealed关键字)。
另外,在一些类含有一些安全方面考虑的成员或者属性,不希望被别的类继承或者改变的时候,可以设计为sealed。

总而言之,言而总之,sealed关键字有一部分用途比如CLR反射时候性能方面的优化,但是最主要的目的应该是为了帮助开发人员尽量避免在设计和编程的过程人为的犯错,让你在连续加班48个小时后保持清晰的思路记得你当初设计的某个不希望被继承改动的类的目的而不犯错,要求是不是太高了!所以,微软的天才们设计了这个关键字,帮助了大家一把,让这个需要注意和提醒的事情,交给了CLR和编译器。

5.    别的不说,我举一个例子,大家很多人都会用static class。这个static其实就是 abstract+sealed。
我需要澄清一点,sealed是CLR和IL层面的东西,和“设计”关系不大。

就好比你可以认为一种高级语言没有必要用到goto,但是如果你设计一个虚拟机,跳转是必须考虑的东西。

6.比如说当我们突然发现从mongodb中反序列化出来的对象实际上无法正常地得到子类对象,而都是仅仅得到了父类对象,而你以前不知道、没注意,现在出现乱子了。你已经没有时间去补充测试所有代码了,你需要在24小时内就发布产品,那么就可以把父类变为sealed的并删除子类,这样调试器就能告诉你哪些代码是出乱子的,而不需要你一点点地去学测试代码。然后当你已经发布了产品,如果你觉得懒得再把子类写回来了,懒得实现原来的既定扩展目标,你根本没有压力需要对类型进行扩展,那么你当然仍然会保留着这个sealed

7.    密封类:到他这一代已经最牛逼了,不需要生孩子来扩展天赋了。

普通类:造物者觉得他还可以通过生孩子来扩展家族天赋。

8.    最明确的就是为了预防一些因继承可能会导致的异常






<think>嗯,用户想知道JDK17中密封类使用场景和示例。首先,我需要回顾一下密封类的基本概念。根据引用[2],密封类允许开发者控制哪些类可以继承或实现某个类或接口,这样可以提供更严格的继承控制,增强代码的安全性和可维护性。 接下来,我得考虑用户可能需要的具体例子。比如,在什么情况下会用到密封类呢?常见的应用场景可能有领域模型设计、状态机、表达式树等。比如在金融系统中,不同的账户类型可能需要不同的处理,这时候密封类可以限制子类,避免其他人添加未授权的类型。 然后,我需要确保示例代码符合Java 17的语法。密封类使用sealed关键字,并且用permits子句指定允许的子类。子类必须是final、sealed或non-sealed。例如,定义一个密封类Shape,允许Circle和Rectangle作为子类,这样其他类就不能继承Shape,除非明确允许。 另外,用户可能想知道密封类如何与模式匹配结合使用,比如在switch语句中,根据不同的子类执行不同的操作。引用[3]提到JDK17支持在switch中使用模式匹配,所以可以举一个使用instanceof检查类型并处理的例子。 还要注意密封类在接口上的应用,比如定义权限系统,允许特定的实现类。这样在编译时就能检查所有可能的子类,避免遗漏case,提高代码的健壮性。 最后,需要总结密封类的优势,如限制继承范围、增强类型安全、便于模式匹配等,并指出适用场景,如需要严格控制继承关系的场景。同时,确保引用正确的文献来源,比如引用[2]和引用[3]。</think>### JDK17 密封类使用场景及示例 #### 1. 密封类核心作用 密封类通过`sealed`关键字和`permits`子句限定可继承的子类,提供**编译期强约束的类继承控制**,适用于需要严格限制继承关系的场景[^2]。 #### 2. 典型使用场景 **场景一:领域模型设计** ```java // 定义图形类型密封类 public sealed abstract class Shape permits Circle, Rectangle, Triangle { // 只允许三个子类 public abstract double area(); } // 具体子类必须声明为 final/sealed/non-sealed final class Circle extends Shape { private final double radius; public double area() { return Math.PI * radius * radius; } } final class Rectangle extends Shape { private final double width, height; public double area() { return width * height; } } non-sealed class Triangle extends Shape { // 允许Triangle被继续扩展 protected double base, height; public double area() { return 0.5 * base * height; } } ``` **优势**:防止出现未定义的图形类型,保证`switch`模式匹配的完整性检查[^3]。 --- **场景二:状态机实现** ```java // 定义工作流状态机 public sealed interface WorkflowState permits Draft, Submitted, Approved, Rejected {} final class Draft implements WorkflowState {} final class Submitted implements WorkflowState {} final class Approved implements WorkflowState {} final class Rejected implements WorkflowState {} ``` **优势**:确保状态流转的合法性,例如从`Draft`只能转换到`Submitted`,避免无效状态出现。 --- **场景三:表达式树处理** ```java // 定义算术表达式结构 public sealed interface Expr permits ConstantExpr, PlusExpr, MinusExpr, TimesExpr {} record ConstantExpr(int value) implements Expr {} record PlusExpr(Expr left, Expr right) implements Expr {} record MinusExpr(Expr left, Expr right) implements Expr {} record TimesExpr(Expr left, Expr right) implements Expr {} ``` **优势**:结合模式匹配特性实现安全计算: ```java static int evaluate(Expr e) { return switch(e) { case ConstantExpr c -> c.value(); case PlusExpr p -> evaluate(p.left()) + evaluate(p.right()); case MinusExpr m -> evaluate(m.left()) - evaluate(m.right()); case TimesExpr t -> evaluate(t.left()) * evaluate(t.right()); // 不需要default分支,编译器验证了所有可能性 }; } ``` --- #### 3. 接口密封控制 ```java // 定义权限系统接口 public sealed interface AuthProvider permits DatabaseAuth, LdapAuth, OAuth2 {} // 限定认证方式 final class DatabaseAuth implements AuthProvider { /* 数据库认证 */ } final class LdapAuth implements AuthProvider { /* LDAP认证 */ } final class OAuth2 implements AuthProvider { /* OAuth2认证 */ } ``` #### 4. 核心优势总结 | 特性 | 传统继承 | 密封类 | |--------------------|------------------------|--------------------------| | 继承控制 | 完全开放 | 白名单控制 | | 模式匹配安全性 | 需要default分支 | 编译器验证分支完整性 | | 代码可维护性 | 容易产生不可控子类 | 明确继承关系树 | **适用场景**:金融领域金额类型、编译器AST节点、协议消息格式等需要严格类型控制的场景[^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值