告别“if-else地狱“!Java 21模式匹配,代码优雅了10倍 告别if-else地狱Java 21模式匹配代码优雅了10倍如果你打开一个方法看到的是50行if-else嵌套你会是什么感受反正我是想砸键盘。大家好我是卷毛。前段时间接手了一个老项目有个订单处理方法if-else嵌套了7层478行代码。我花了3天重构完用的是什么Java 21的模式匹配。今天这篇文章我把自己实际用到的5种重构手法分享出来每种都有真实代码对比保证你看完就能用。先看个恐怖故事这是真实代码我只改了变量名// 重构前 —— 478行的怪物publicvoidprocessOrder(Orderorder){if(order!null){if(order.getType()OrderType.NORMAL){if(order.getStatus()OrderStatus.PENDING){if(order.getAmount()1000){if(order.getUser().getLevel()UserLevel.VIP){// VIP大额待处理订单if(order.getPaymentMethod()PaymentMethod.WECHAT){vipWechatLargeOrderProcess(order);}elseif(order.getPaymentMethod()PaymentMethod.ALIPAY){vipAlipayLargeOrderProcess(order);}else{vipOtherLargeOrderProcess(order);}}else{// 普通用户大额待处理订单normalLargeOrderProcess(order);}}else{// 小额订单处理...// 还有3层嵌套...}}elseif(order.getStatus()OrderStatus.PAID){// 已支付订单处理...}elseif(order.getStatus()OrderStatus.CANCELLED){// 取消订单处理...}}elseif(order.getType()OrderType.GROUP){// 团购订单...// 又是几层嵌套...}}}看完是不是血压上来了别急我们一步步来。手法一instanceof模式匹配 —— 干掉强转痛点instanceof判断完还要强转啰嗦。// ❌ 以前if(objinstanceofString){Stringstr(String)obj;// 多余的强转System.out.println(str.length());}// ✅ Java 21if(objinstanceofStringstr){System.out.println(str.length());}实战场景// 处理不同类型的消息体publicvoidhandleMessage(Objectmessage){if(messageinstanceofTextMessagetm){textProcessor.process(tm.getContent());}elseif(messageinstanceofImageMessageim){imageProcessor.process(im.getUrl(),im.getWidth(),im.getHeight());}elseif(messageinstanceofVideoMessagevm){videoProcessor.process(vm.getUrl(),vm.getDuration());}elseif(messageinstanceofnull){log.warn(收到空消息);}}一行代码省掉了声明强转10个分支就省10行。手法二switch模式匹配 —— 替代if-else链痛点基于类型的分支判断if-else写起来又长又丑。// ❌ 以前 —— 类型判断强转又臭又长publicdoublecalculateArea(Shapeshape){if(shapeinstanceofCircle){Circlec(Circle)shape;returnMath.PI*c.getRadius()*c.getRadius();}elseif(shapeinstanceofRectangle){Rectangler(Rectangle)shape;returnr.getWidth()*r.getHeight();}elseif(shapeinstanceofTriangle){Trianglet(Triangle)shape;doubles(t.getA()t.getB()t.getC())/2;returnMath.sqrt(s*(s-t.getA())*(s-t.getB())*(s-t.getC()));}else{thrownewIllegalArgumentException(未知形状: shape.getClass().getName());}}// ✅ Java 21 —— switch模式匹配publicdoublecalculateArea(Shapeshape){returnswitch(shape){caseCirclec-Math.PI*c.radius()*c.radius();caseRectangler-r.width()*r.height();caseTrianglet-{doubles(t.a()t.b()t.c())/2;yieldMath.sqrt(s*(s-t.a())*(s-t.b())*(s-t.c()));}casenull-thrownewIllegalArgumentException(形状不能为null);default-thrownewIllegalArgumentException(未知形状);};}注意switch模式匹配要求穷举所有可能如果你用了sealed class编译器会帮你检查。手法三Record 模式匹配 —— 替代DTO地狱痛点处理不同类型的消息/事件DTO类一堆处理逻辑if-else一堆。先定义sealed接口和Record// 定义一个密封接口限定实现类型publicsealedinterfacePaymentEventpermitsPaymentSuccess,PaymentFailed,RefundProcessed{}publicrecordPaymentSuccess(StringorderId,BigDecimalamount,StringtradeNo)implementsPaymentEvent{}publicrecordPaymentFailed(StringorderId,Stringreason,intretryCount)implementsPaymentEvent{}publicrecordRefundProcessed(StringorderId,BigDecimalrefundAmount,StringrefundNo)implementsPaymentEvent{}处理逻辑// 简洁、类型安全、编译器保证穷举publicvoidhandlePaymentEvent(PaymentEventevent){switch(event){casePaymentSuccess(StringorderId,BigDecimalamount,StringtradeNo)-orderService.confirmPayment(orderId,amount,tradeNo);casePaymentFailed(StringorderId,Stringreason,intretryCount)when retryCount3-retryService.scheduleRetry(orderId,retryCount1);casePaymentFailed(StringorderId,Stringreason,intretryCount)-notifyService.notifyFailure(orderId,reason);caseRefundProcessed(StringorderId,BigDecimalrefundAmount,StringrefundNo)-refundService.completeRefund(orderId,refundAmount,refundNo);}}注意看when retryCount 3这个guard条件——以前要用if嵌套实现的逻辑现在一行搞定。Record解构的模式匹配对比// Record组件可以直接解构casePaymentSuccess(StringorderId,BigDecimalamount,StringtradeNo)// 也可以只绑定部分组件用 _ 忽略不关心的casePaymentSuccess(StringorderId,_,_)// 甚至全忽略casePaymentSuccess(_,_,_)手法四null安全模式匹配 —— 告别NPE恐惧痛点null检查散落各处漏了一个就NPE。// ❌ 以前 —— 各种null检查publicStringgetUserName(Useruser){if(usernull){return匿名用户;}if(user.getProfile()null){returnuser.getName();}if(user.getProfile().getNickname()null){returnuser.getName();}returnuser.getProfile().getNickname();}// ✅ Java 21 —— null模式匹配publicStringgetUserName(Useruser){returnswitch(user){casenull-匿名用户;caseUseru when u.profile()null-u.name();caseUseru when u.profile().nickname()null-u.name();caseUseru-u.profile().nickname();};}配合Optional更优雅publicStringgetUserName(Useruser){returnOptional.ofNullable(user).map(User::getProfile).map(Profile::getNickname).orElseGet(()-Optional.ofNullable(user).map(User::getName).orElse(匿名用户));}手法五重构开头的恐怖故事用上面4种手法组合478行变62行// 重构后 —— 62行清晰可维护publicvoidprocessOrder(Orderorder){switch(order){casenull-log.warn(收到空订单);caseOrdero when o.type()OrderType.NORMAL-processNormalOrder(o);caseOrdero when o.type()OrderType.GROUP-processGroupOrder(o);}}privatevoidprocessNormalOrder(Orderorder){switch(order){caseOrder(varid,_,varamount,varstatus,varuser,varpayment)when statusOrderStatus.PENDINGamount.compareTo(BigDecimal.valueOf(1000))0user.level()UserLevel.VIP-processVipLargeOrder(order,payment);caseOrder(_,_,varamount,varstatus,varuser,_)when statusOrderStatus.PENDINGamount.compareTo(BigDecimal.valueOf(1000))0-normalLargeOrderProcess(order);caseOrder(_,_,_,varstatus,_,_)when statusOrderStatus.PAID-paidOrderProcess(order);caseOrder(_,_,_,varstatus,_,_)when statusOrderStatus.CANCELLED-cancelledOrderProcess(order);default-pendingSmallOrderProcess(order);}}privatevoidprocessVipLargeOrder(Orderorder,PaymentMethodpayment){switch(payment){caseWECHAT-vipWechatLargeOrderProcess(order);caseALIPAY-vipAlipayLargeOrderProcess(order);default-vipOtherLargeOrderProcess(order);}}478行 → 62行而且每一行都在说做什么而不是怎么判断。重构前后对比维度重构前重构后代码行数478行62行最大嵌套层级7层2层圈复杂度348新增分支成本在大方法里加if-else加一个case可测试性差一个方法测所有分支好每个子方法独立测试可读性需要跟踪嵌套逻辑顺序阅读一目了然实操建议先加测试再重构模式匹配改变的是代码结构不是逻辑测试是安全网优先用sealed class编译器帮你检查穷举少一个case直接编译报错guard条件别写太复杂when后面超过2个条件就考虑提取方法Record优先新代码用Record替代传统的DTO/VO配合模式匹配效果最好不要为了用而用简单的if-else没必要硬改成switch工具是解决问题的不是炫技的写在最后模式匹配不是什么新潮语法糖它是Java在向函数式编程范式靠拢的重要一步。用了之后你会发现代码不只是变短了而是变清晰了——你在表达做什么而不是怎么做。9年Java开发我越来越觉得好代码不是写得巧妙而是写得让人一看就懂。我是卷毛9年Java开发专注Java技术实战分享。这篇文章如果对你有帮助点个「在看」和「关注」是我持续输出的最大动力。下一篇我们聊虚拟线程的实战踩坑经验关注不迷路《卷毛的技术笔记》—— 一起卷出技术力。