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

告别“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 retryCount<3->retryService.scheduleRetry(orderId,retryCount+1);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(user==null){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 status==OrderStatus.PENDING&&amount.compareTo(BigDecimal.valueOf(1000))>0&&user.level()==UserLevel.VIP->processVipLargeOrder(order,payment);caseOrder(_,_,varamount,varstatus,varuser,_)when status==OrderStatus.PENDING&&amount.compareTo(BigDecimal.valueOf(1000))>0->normalLargeOrderProcess(order);caseOrder(_,_,_,varstatus,_,_)when status==OrderStatus.PAID->paidOrderProcess(order);caseOrder(_,_,_,varstatus,_,_)when status==OrderStatus.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
可测试性差,一个方法测所有分支好,每个子方法独立测试
可读性需要跟踪嵌套逻辑顺序阅读,一目了然

实操建议

  1. 先加测试再重构:模式匹配改变的是代码结构,不是逻辑,测试是安全网
  2. 优先用sealed class:编译器帮你检查穷举,少一个case直接编译报错
  3. guard条件别写太复杂when后面超过2个条件就考虑提取方法
  4. Record优先:新代码用Record替代传统的DTO/VO,配合模式匹配效果最好
  5. 不要为了用而用:简单的if-else没必要硬改成switch,工具是解决问题的不是炫技的

写在最后

模式匹配不是什么"新潮语法糖",它是Java在向函数式编程范式靠拢的重要一步。用了之后你会发现,代码不只是变短了,而是变清晰了——你在表达"做什么",而不是"怎么做"。

9年Java开发,我越来越觉得:好代码不是写得巧妙,而是写得让人一看就懂。


📌我是卷毛,9年Java开发,专注Java技术实战分享。

这篇文章如果对你有帮助,点个「在看」和「关注」,是我持续输出的最大动力。

下一篇我们聊虚拟线程的实战踩坑经验,关注不迷路!

《卷毛的技术笔记》—— 一起卷出技术力。🔥

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

相关文章:

  • 【ESP32实战】告别烧录:U8g2 UI在线仿真与高效调试指南
  • 智能化桌面助手 OpenClaw 部署手册,双系统通用操作步骤(含安装包)
  • RePKG深度解析:Wallpaper Engine资源处理的专业技术指南
  • 3分钟学会视频PPT提取:快速从视频中抓取演示文稿的完整指南
  • 魔兽世界API与宏工具:3分钟掌握游戏开发与战斗优化终极指南 [特殊字符]
  • 从尾部丢弃到智能预警:RED/WRED如何破解TCP全局同步难题
  • 外贸企业邮箱选型避坑:做外贸用什么邮箱好?主流邮箱跨境投递深度测评
  • Kiran图标主题的目录结构与组织架构详解
  • CAXA下载教程CAXA电子图版2024 保姆级安装步骤(附安装包)
  • Go语言性能封神!10行代码解决高并发接口卡顿问题
  • TPC-H基准测试工具:从源码编译到数据生成的实战指南
  • Shell脚本精读 · S05-03 | `[[` 与模式匹配:Bash 条件表达式
  • 星元素甄选的“底层逻辑”:不靠信息差赚钱,靠效率赢信任
  • GEO优化与AI客流的提前布局,在什么时间点开展最合适?
  • 工业品短视频代运营/询盘不断还主动转介绍客户!靠谱工业品短视频代运营靠效果说话
  • 如何5分钟配置DS4Windows:让PS手柄在Windows上完美运行的终极指南
  • 公证需要去哪里办理?常见公证事项要准备哪些材料?
  • WarcraftHelper完整指南:魔兽争霸3终极免费辅助工具,彻底解决兼容性问题
  • 华为OD机试2025C卷-乘坐保密电梯[100分](Java_Python3_C++_C语言_JsNode_Go)实现100%通过率
  • 解锁CUDA Warp Shuffle:高效线程间数据交换的实战指南
  • 5分钟解锁QQ音乐加密音频:qmcdump无损转换终极指南
  • SpringBoot DTO参数校验:从基础注解到自定义规则的实战指南
  • WorkshopDL深度解析:如何跨平台获取Steam创意工坊模组
  • 【HCIA-AI笔记(微认证2)】1.2 DeepSeek训练过程介绍
  • MAX30102传感器实战:从寄存器配置到心率血氧数据采集
  • AXI协议——1.1. 从总线到接口:AXI协议全景解析
  • 质谱原理及生态
  • HyperWorks OptiStruct几何非线性的设置
  • utwget重构解析:如何用Rust打造下一代高效网络下载工具
  • 如何在3分钟内免费为Windows系统换上macOS风格鼠标指针:完整美化教程