当前位置: 首页 > news >正文

【Java SE】sealed关键字

sealed关键字

  • 为什么需要sealed?
  • sealed类基础语法
    • 基本声明
    • 子类的三种修饰符⭐
  • sealed接口
    • 与模式匹配的完美配合
  • 实际应用场景
    • 领域驱动设计(DDD)中的值对象
    • 表达式求值器
    • JSON值类型建模
  • 约束与限制
    • 包内可见性规则
    • 不能与final同时使用
  • 与传统模式的对比
  • 最佳实践
    • 在API设计中优先使用sealed
    • 结合record使用
    • 使用non-sealed的时机

在Java 15(预览)/ Java 17(正式)中,sealed关键字的引入为Java的类型系统带来了革命性的变化。它允许开发者精确控制哪些类可以继承或实现某个类型,在开放性和封闭性之间找到了完美的平衡点。

为什么需要sealed?

传统的Java类继承机制存在一个两难困境:

  • 使用final:完全禁止继承,过于严格
  • 不添加限制:任何人都可以继承,可能导致滥用
// 传统方案的问题publicclassAnimal{}// 任何人都可以继承publicfinalclassCatextendsAnimal{}// 完全禁止继承

在实际开发中,我们常常需要一种受控的继承:明确知道哪些子类存在,同时允许这些子类自由扩展。这正是sealed关键字要解决的问题。

sealed类基础语法

基本声明

使用sealed关键字声明一个类或接口,并通过permits子句列出允许的子类型:

// 定义sealed类,只允许Dog和Cat继承publicsealedclassAnimalpermitsDog,Cat{protectedStringname;publicAnimal(Stringname){this.name=name;}publicabstractStringmakeSound();}// 子类可以是finalfinalclassDogextendsAnimal{publicDog(Stringname){super(name);}@OverridepublicStringmakeSound(){return"Woof!";}}// 子类可以是非sealed(允许进一步继承)non-sealedclassCatextendsAnimal{publicCat(Stringname){super(name);}@OverridepublicStringmakeSound(){return"Meow!";}}// 子类也可以是sealedsealedclassBirdextendsAnimalpermitsSparrow,Eagle{publicBird(Stringname){super(name);}}

子类的三种修饰符⭐

sealed类的子类必须声明以下三种修饰符之一:

修饰符含义适用场景
final禁止进一步继承叶子节点类
sealed继续受限继承需要进一步细分的中间类
non-sealed恢复开放性允许任意扩展
// 完整的继承层次示例publicsealedclassShapepermitsCircle,Rectangle,Triangle{publicabstractdoublearea();}// final:不能再被继承finalclassCircleextendsShape{privatedoubleradius;publicCircle(doubleradius){this.radius=radius;}@Overridepublicdoublearea(){returnMath.PI*radius*radius;}}// sealed:继续受限继承sealedclassRectangleextendsShapepermitsSquare{protecteddoublewidth;protecteddoubleheight;publicRectangle(doublewidth,doubleheight){this.width=width;this.height=height;}@Overridepublicdoublearea(){returnwidth*height;}}finalclassSquareextendsRectangle{publicSquare(doubleside){super(side,side);}}// non-sealed:恢复开放性non-sealedclassTriangleextendsShape{privatedoublebase;privatedoubleheight;publicTriangle(doublebase,doubleheight){this.base=base;this.height=height;}@Overridepublicdoublearea(){return0.5*base*height;}}// Triangle是non-sealed,所以可以任意继承classRightTriangleextendsTriangle{publicRightTriangle(doublebase,doubleheight){super(base,height);}}

sealed接口

sealed同样适用于接口,这在定义领域模型时尤为有用:

// 定义sealed接口publicsealedinterfacePaymentMethodpermitsCreditCard,PayPal,WeChatPay{voidpay(BigDecimalamount);StringgetTransactionId();}// 实现类可以是record(record默认final)recordCreditCard(StringcardNumber,Stringcvv)implementsPaymentMethod{@Overridepublicvoidpay(BigDecimalamount){System.out.println("支付 "+amount+" 元,使用信用卡: "+cardNumber);}@OverridepublicStringgetTransactionId(){return"CC_"+UUID.randomUUID();}}recordPayPal(Stringemail)implementsPaymentMethod{@Overridepublicvoidpay(BigDecimalamount){System.out.println("支付 "+amount+" 元,使用PayPal: "+email);}@OverridepublicStringgetTransactionId(){return"PP_"+UUID.randomUUID();}}non-sealedclassWeChatPayimplementsPaymentMethod{privateStringopenId;publicWeChatPay(StringopenId){this.openId=openId;}@Overridepublicvoidpay(BigDecimalamount){System.out.println("支付 "+amount+" 元,使用微信支付: "+openId);}@OverridepublicStringgetTransactionId(){return"WX_"+System.currentTimeMillis();}}

与模式匹配的完美配合

sealed类与Java的模式匹配(Pattern Matching)是天作之合。由于编译器知道所有可能的子类型,可以实现穷举性检查

publicclassPaymentProcessor{// 使用模式匹配处理支付publicstaticStringprocessPayment(PaymentMethodpayment){// 编译器可以验证所有情况都已覆盖returnswitch(payment){caseCreditCardcc->String.format("处理信用卡支付: %s",cc.cardNumber());casePayPalpp->String.format("处理PayPal支付: %s",pp.email());caseWeChatPaywp->String.format("处理微信支付: %s","交易完成");};}// 处理sealed类的继承层次publicstaticdoublecalculateArea(Shapeshape){returnswitch(shape){caseCirclec->Math.PI*c.radius()*c.radius();caseRectangler->r.width()*r.height();caseSquares->s.side()*s.side();caseTrianglet->0.5*t.base()*t.height();};}}

实际应用场景

领域驱动设计(DDD)中的值对象

// 定义货币类型,只允许特定的币种publicsealedinterfaceCurrencypermitsUSD,EUR,CNY,JPY{StringgetCode();StringgetSymbol();}recordUSD()implementsCurrency{@OverridepublicStringgetCode(){return"USD";}@OverridepublicStringgetSymbol(){return"$";}}recordEUR()implementsCurrency{@OverridepublicStringgetCode(){return"EUR";}@OverridepublicStringgetSymbol(){return"€";}}recordCNY()implementsCurrency{@OverridepublicStringgetCode(){return"CNY";}@OverridepublicStringgetSymbol(){return"¥";}}recordJPY()implementsCurrency{@OverridepublicStringgetCode(){return"JPY";}@OverridepublicStringgetSymbol(){return"¥";}}

表达式求值器

// 定义表达式类型,只允许三种表达式publicsealedinterfaceExpressionpermitsConstant,Addition,Multiplication{doubleevaluate();}recordConstant(doublevalue)implementsExpression{@Overridepublicdoubleevaluate(){returnvalue;}}recordAddition(Expressionleft,Expressionright)implementsExpression{@Overridepublicdoubleevaluate(){returnleft.evaluate()+right.evaluate();}}recordMultiplication(Expressionleft,Expressionright)implementsExpression{@Overridepublicdoubleevaluate(){returnleft.evaluate()*right.evaluate();}}// 使用示例publicclassCalculator{publicstaticvoidmain(String[]args){// 表达式: (3 + 4) * 5Expressionexpr=newMultiplication(newAddition(newConstant(3),newConstant(4)),newConstant(5));System.out.println("计算结果: "+expr.evaluate());// 35.0}}

JSON值类型建模

// 定义JSON值的所有可能类型publicsealedinterfaceJsonValuepermitsJsonNull,JsonBoolean,JsonNumber,JsonString,JsonArray,JsonObject{}recordJsonNull()implementsJsonValue{}recordJsonBoolean(booleanvalue)implementsJsonValue{}recordJsonNumber(doublevalue)implementsJsonValue{}recordJsonString(Stringvalue)implementsJsonValue{}recordJsonArray(List<JsonValue>values)implementsJsonValue{}recordJsonObject(Map<String,JsonValue>properties)implementsJsonValue{}// 类型安全的JSON解析器publicclassJsonParser{publicstaticStringtoJsonString(JsonValuevalue){returnswitch(value){caseJsonNull()->"null";caseJsonBoolean(varb)->Boolean.toString(b);caseJsonNumber(varn)->Double.toString(n);caseJsonString(vars)->"\""+s+"\"";caseJsonArray(vararr)->{varelements=arr.stream().map(JsonParser::toJsonString).collect(Collectors.joining(","));yield"["+elements+"]";}caseJsonObject(varprops)->{varentries=props.entrySet().stream().map(e->"\""+e.getKey()+"\":"+toJsonString(e.getValue())).collect(Collectors.joining(","));yield"{"+entries+"}";}};}}

约束与限制

包内可见性规则

permits子句中列出的类必须在同一个模块中,或者与sealed类在同一个包内:

// 同一个包内packagecom.example.shapes;publicsealedclassShapepermitsCircle,Rectangle{// ...}// 必须在同一个包内finalclassCircleextendsShape{}finalclassRectangleextendsShape{}

不能与final同时使用

sealed和final是互斥的修饰符:

// ❌ 错误:sealed和final不能同时使用publicsealedfinalclassMyClass{}// ✅ 正确:sealed类可以有final子类publicsealedclassMyClasspermitsSubClass{}finalclassSubClassextendsMyClass{}

与传统模式的对比

特性传统继承sealed类
子类控制无限制精确控制
模式匹配穷举无法保证编译器保证
API设计容易暴露实现明确公开契约
向后兼容性难以限制通过permits控制

最佳实践

在API设计中优先使用sealed

// 定义公开的APIpublicsealedinterfaceResult<T>permitsSuccess,Failure{TgetValue();}recordSuccess<T>(Tdata)implementsResult<T>{@OverridepublicTgetValue(){returndata;}}recordFailure<T>(StringerrorMessage)implementsResult<T>{@OverridepublicTgetValue(){thrownewIllegalStateException("Cannot get value from failure");}}

结合record使用

record与sealed的配合是天作之合:

publicsealedinterfaceHttpRequestpermitsGetRequest,PostRequest,PutRequest,DeleteRequest{Stringpath();Map<String,String>headers();}recordGetRequest(Stringpath,Map<String,String>headers)implementsHttpRequest{}recordPostRequest(Stringpath,Map<String,String>headers,Stringbody)implementsHttpRequest{}recordPutRequest(Stringpath,Map<String,String>headers,Stringbody)implementsHttpRequest{}recordDeleteRequest(Stringpath,Map<String,String>headers)implementsHttpRequest{}

使用non-sealed的时机

只在确实需要让第三方扩展时才使用non-sealed

// 框架提供的抽象类,允许用户扩展publicsealedclassFrameworkPluginpermitsDatabasePlugin,CachePlugin,non-sealedUserPlugin{publicabstractvoidinitialize();}// 允许用户自定义扩展publicclassCustomPluginextendsFrameworkPlugin{@Overridepublicvoidinitialize(){// 用户自定义逻辑}}

sealed不是要取代传统的继承,而是在开放和封闭之间提供了更精细的控制粒度。在Java 17及以后的版本中,sealed类与record、模式匹配等特性共同构成了现代Java函数式编程的基础,让Java语言在保持面向对象特性的同时,向更安全、更简洁的方向持续演进。

http://www.jsqmd.com/news/556284/

相关文章:

  • 基于Transformer的单变量时序预测:Matlab实战指南
  • Agent应用开发相关知识梳理——1.LangChain框架理解
  • DAMOYOLO-S快速部署:GPU实例选择建议与显存占用实测数据
  • Python恶搞神器:用tkinter和threading打造随机位置无限弹窗
  • 如何用Qwen3-ASR-1.7B为视频自动生成字幕?实战教程来了
  • KS-Downloader:快手无水印内容获取工具全解析
  • 最强翻译模型Hunyuan-MT-7B一键部署:5分钟搞定33种语言互译
  • TrollInstallerX深度解析:iOS 14.0-16.6.1设备上的TrollStore安装实战指南
  • Music-dl实战指南:多平台音乐下载工具的高效部署与优化方案
  • Vue3下拉刷新组件实战:从零封装到全局注册(附完整代码)
  • LeetCode 2839. 判断通过操作能否让字符串相等 I(Python)超详细题解|贪心算法+模拟
  • Jimeng AI Studio Z-Image Turbo部署教程:A10/A100云服务器高性能配置
  • Equalizer APO:3个步骤让Windows音频效果提升200%
  • 网盘直链解析引擎:打破下载速度壁垒的技术方案
  • etcd和brpc的联合运作在即使通讯系统中的原理
  • Windows 环境下利用 nmap 进行 UDP 端口连通性测试实战
  • PostgresSQL 更改数据库存储目录
  • uni-app微信小程序版本更新策略:冷启动与热启动的优化实践
  • 摊铺机液压系统及组件系列图
  • OBS Studio架构深度解析:如何构建专业级直播系统的核心技术栈
  • Nano-Banana软萌拆拆屋效果展示:潜水服密封拉链结构图
  • 掌控 OpenClaw:核心命令行
  • 轻量级向量引擎:SQLite-vec赋能Java应用的极简方案
  • 3/15
  • 从零构建SST实验范式:基于PsychoPy的抑制控制测量实践
  • 从素材到成片:AI 一站式极速输出——影视创作的新时代革命
  • ARMv8-A中断处理避坑指南:GICv3配置与多核中断路由那些事儿
  • 梳理资料分析的相关知识点
  • 时间管理大师:OpenClaw+nanobot自动规划每日日程
  • 嵌入式Linux开发板CH340驱动安装避坑指南(附详细步骤图)