sealed关键字为什么需要sealedsealed类基础语法基本声明子类的三种修饰符⭐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.namename;}publicabstractStringmakeSound();}// 子类可以是finalfinalclassDogextendsAnimal{publicDog(Stringname){super(name);}OverridepublicStringmakeSound(){returnWoof!;}}// 子类可以是非sealed允许进一步继承non-sealedclassCatextendsAnimal{publicCat(Stringname){super(name);}OverridepublicStringmakeSound(){returnMeow!;}}// 子类也可以是sealedsealedclassBirdextendsAnimalpermitsSparrow,Eagle{publicBird(Stringname){super(name);}}子类的三种修饰符⭐sealed类的子类必须声明以下三种修饰符之一修饰符含义适用场景final禁止进一步继承叶子节点类sealed继续受限继承需要进一步细分的中间类non-sealed恢复开放性允许任意扩展// 完整的继承层次示例publicsealedclassShapepermitsCircle,Rectangle,Triangle{publicabstractdoublearea();}// final不能再被继承finalclassCircleextendsShape{privatedoubleradius;publicCircle(doubleradius){this.radiusradius;}Overridepublicdoublearea(){returnMath.PI*radius*radius;}}// sealed继续受限继承sealedclassRectangleextendsShapepermitsSquare{protecteddoublewidth;protecteddoubleheight;publicRectangle(doublewidth,doubleheight){this.widthwidth;this.heightheight;}Overridepublicdoublearea(){returnwidth*height;}}finalclassSquareextendsRectangle{publicSquare(doubleside){super(side,side);}}// non-sealed恢复开放性non-sealedclassTriangleextendsShape{privatedoublebase;privatedoubleheight;publicTriangle(doublebase,doubleheight){this.basebase;this.heightheight;}Overridepublicdoublearea(){return0.5*base*height;}}// Triangle是non-sealed所以可以任意继承classRightTriangleextendsTriangle{publicRightTriangle(doublebase,doubleheight){super(base,height);}}sealed接口sealed同样适用于接口这在定义领域模型时尤为有用// 定义sealed接口publicsealedinterfacePaymentMethodpermitsCreditCard,PayPal,WeChatPay{voidpay(BigDecimalamount);StringgetTransactionId();}// 实现类可以是recordrecord默认finalrecordCreditCard(StringcardNumber,Stringcvv)implementsPaymentMethod{Overridepublicvoidpay(BigDecimalamount){System.out.println(支付 amount 元使用信用卡: cardNumber);}OverridepublicStringgetTransactionId(){returnCC_UUID.randomUUID();}}recordPayPal(Stringemail)implementsPaymentMethod{Overridepublicvoidpay(BigDecimalamount){System.out.println(支付 amount 元使用PayPal: email);}OverridepublicStringgetTransactionId(){returnPP_UUID.randomUUID();}}non-sealedclassWeChatPayimplementsPaymentMethod{privateStringopenId;publicWeChatPay(StringopenId){this.openIdopenId;}Overridepublicvoidpay(BigDecimalamount){System.out.println(支付 amount 元使用微信支付: openId);}OverridepublicStringgetTransactionId(){returnWX_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(){returnUSD;}OverridepublicStringgetSymbol(){return$;}}recordEUR()implementsCurrency{OverridepublicStringgetCode(){returnEUR;}OverridepublicStringgetSymbol(){return€;}}recordCNY()implementsCurrency{OverridepublicStringgetCode(){returnCNY;}OverridepublicStringgetSymbol(){return¥;}}recordJPY()implementsCurrency{OverridepublicStringgetCode(){returnJPY;}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) * 5ExpressionexprnewMultiplication(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(ListJsonValuevalues)implementsJsonValue{}recordJsonObject(MapString,JsonValueproperties)implementsJsonValue{}// 类型安全的JSON解析器publicclassJsonParser{publicstaticStringtoJsonString(JsonValuevalue){returnswitch(value){caseJsonNull()-null;caseJsonBoolean(varb)-Boolean.toString(b);caseJsonNumber(varn)-Double.toString(n);caseJsonString(vars)-\s\;caseJsonArray(vararr)-{varelementsarr.stream().map(JsonParser::toJsonString).collect(Collectors.joining(,));yield[elements];}caseJsonObject(varprops)-{varentriesprops.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// 定义公开的APIpublicsealedinterfaceResultTpermitsSuccess,Failure{TgetValue();}recordSuccessT(Tdata)implementsResultT{OverridepublicTgetValue(){returndata;}}recordFailureT(StringerrorMessage)implementsResultT{OverridepublicTgetValue(){thrownewIllegalStateException(Cannot get value from failure);}}结合record使用record与sealed的配合是天作之合publicsealedinterfaceHttpRequestpermitsGetRequest,PostRequest,PutRequest,DeleteRequest{Stringpath();MapString,Stringheaders();}recordGetRequest(Stringpath,MapString,Stringheaders)implementsHttpRequest{}recordPostRequest(Stringpath,MapString,Stringheaders,Stringbody)implementsHttpRequest{}recordPutRequest(Stringpath,MapString,Stringheaders,Stringbody)implementsHttpRequest{}recordDeleteRequest(Stringpath,MapString,Stringheaders)implementsHttpRequest{}使用non-sealed的时机只在确实需要让第三方扩展时才使用non-sealed// 框架提供的抽象类允许用户扩展publicsealedclassFrameworkPluginpermitsDatabasePlugin,CachePlugin,non-sealedUserPlugin{publicabstractvoidinitialize();}// 允许用户自定义扩展publicclassCustomPluginextendsFrameworkPlugin{Overridepublicvoidinitialize(){// 用户自定义逻辑}}sealed不是要取代传统的继承而是在开放和封闭之间提供了更精细的控制粒度。在Java 17及以后的版本中sealed类与record、模式匹配等特性共同构成了现代Java函数式编程的基础让Java语言在保持面向对象特性的同时向更安全、更简洁的方向持续演进。