首页 / 资讯中心 / 文章详情

写个订单类要 55 行?Java 21 的 record 一行搞定

写个订单类要 55 行?Java 21 的 record 一行搞定 ★ FEATURED ARTICLE
写个订单类要 55 行Java 21 的 record 一行搞定系列Java 后端实战精通营 · 作者鱼宵配套课程仓库https://gitee.com/j67mk2/java21-journey 第 4 课先讲一个加个字段改出 bug的真实现场你肯定见过这种代码一个订单类字段七八个构造器、getter、equals、hashCode、toString 手写五六十行。然后某天需求来了——“订单加一个优惠券字段”。手快的同学在字段区加了一行getter 加了一行忘了改 equals。于是两笔内容完全不同的订单一个用了优惠券、一个没用equals返回true——去重逻辑直接失效同一个订单被当成两个库存扣了两遍。这个 bug 的根源不是粗心是样板代码全靠手写字段一变就失同步。Java 21 的record就是冲着这个来的。一句话结论record 纯数据类语法糖样板代码全自动Record记录类你只声明有哪几个字段构造器、equals、hashCode、toString、访问器由编译器自动生成字段隐式final不可变。类比智能表格。以前写订单类像一笔一笔手抄账字段、构造器、getter、equals、toString 六七十行record 只写有哪几列剩下的账房先生编译器自动算好你想抄错都没机会。关键代码55 行 POJO vs 一行 record完整可运行先看麻烦长什么样——传统手写 POJO 的核心部分publicfinalclassLegacyOrder{privatefinalStringorderId;privatefinalStringitem;privatefinaldoubleamount;// 手写构造器publicLegacyOrder(StringorderId,Stringitem,doubleamount){this.orderIdorderId;this.itemitem;this.amountamount;}// 手写 getter ×3getOrderId() / getItem() / getAmount()publicStringgetOrderId(){returnorderId;}publicStringgetItem(){returnitem;}publicdoublegetAmount(){returnamount;}// 手写 equals每个字段都比一遍字段多了必漏Overridepublicbooleanequals(Objecto){if(thiso)returntrue;if(!(oinstanceofLegacyOrderthat))returnfalse;returnDouble.compare(that.amount,amount)0orderId.equals(that.orderId)item.equals(that.item);}// 手写 hashCode toString各十几行…… 全加起来约 55 行}再看 record——同样的事一行声明/** * record一行声明三个字段构造器/equals/hashCode/toString/访问器全部自动生成 * 字段默认 final不可变类默认 final不能继承 */publicrecordModernOrder(StringorderId,Stringitem,doubleamount){/** 紧凑构造器不写参数列表只写校验/清洗赋值编译器自动补 */publicModernOrder{if(amount0){thrownewIllegalArgumentException(金额不能为负amount);}if(itemnull||item.isBlank()){thrownewIllegalArgumentException(商品名不能为空);}orderIdorderIdnull?null:orderId.trim();// 清洗去空格}}这段代码讲什么三句话record ModernOrder(String orderId, String item, double amount)一行声明三个字段。编译器自动生成构造器、三个访问器、equals、hashCode、toString——五六十行样板一行搞定。紧凑构造器public ModernOrder { ... }record 专属写法没有参数列表只写校验和清洗逻辑。金额为负、商品名为空在对象出生那一刻就被拦下——非法对象根本建不出来。这比先 new 出来再慢慢校验安全一个量级。orderId orderId.trim()在紧凑构造器里给参数重新赋值编译器会把它当作最终存进字段的值。不写这行传进来的 A003 会连空格一起存进去。访问器注意record 的访问器是r1.item()和字段同名的方法不是getItem()——写getItem()编译直接报错。顺带把 sealed 密封类也讲了下一课的地基sealed密封关键字“谁能继承/实现我由我自己说了算”搭配permits列出允许的名单/** sealed 接口只允许名单上的三个形状实现 */publicsealedinterfaceShapepermitsCircle,Rectangle,Square{doublearea();// 每种形状自己算面积/** 固定骨架先打形状名、再打面积模板方法 */defaultStringdescribe(){returnthis.getClass().getSimpleName()面积 String.format(%.2f,area());}}recordCircle(doubleradius)implementsShape{publicdoublearea(){returnMath.PI*radius*radius;}}recordRectangle(doublewidth,doubleheight)implementsShape{publicdoublearea(){returnwidth*height;}}recordSquare(doubleside)implementsShape{publicdoublearea(){returnside*side;}}类比银行 VIP 室的准入名单。普通接口是谁都能进sealed 是门口挂了块牌子“只认名单上这三家”名单外的人想实现它编译直接报错。为什么这很重要——穷尽性exhaustivenesssealed 把子类名单固定下来之后编译器才敢替你做漏没漏检查。名单上就 Circle、Rectangle、Square 三个你的分派代码把三个都覆盖了就叫穷尽——这也是下一课模式匹配 switch 能省略default的前提。没有 sealed别人随时加个新实现类你的 if-else 链就漏了。类比点名册。名单上就三个人点全了才叫穷尽——少点一个老师编译器当场喊出来。实测输出equals 自动比较、非法订单被拦我本机JDK 21.0.12.1的真实输出 第 4 课Record 与密封类 --- 1) 传统 POJO 打印手写的 toString--- l1 LegacyOrder{orderIdA001, item机械键盘, amount299.0} --- 2) record 打印编译器自动生成的 toString--- r1 ModernOrder[orderIdA001, item机械键盘, amount299.0] --- 3) equals 与 hashCode值相等就该相等 --- r1.equals(r2)内容相同 true r1.equals(r3)内容不同 false r1.hashCode() -1835803728r2.hashCode() -1835803728相等对象哈希值相同true --- 4) 紧凑构造器校验负数订单应该被拒绝 --- 捕获到异常金额不能为负-50.0 捕获到异常商品名不能为空 传入 orderId A003 实际存进去的是[A003] --- 5) sealed Shape形状家族子类被 permits 圈定 --- Circle面积 78.54 Rectangle面积 24.00 Square面积 9.00 总面积 111.54输出怎么读ModernOrder[...]是 record 自动生成的 toString方括号 字段名值打印出来一目了然。r1.equals(r2) true、r1.equals(r3) false自动 equals 逐字段比较——回到开头的 bugrecord 从根上消灭了加字段忘了改 equals这类事故。两行 hashCode 相同相等对象哈希值也相等放 HashMap 当 key 不会翻车。两条捕获到异常紧凑构造器把金额为负、商品名为空的订单在创建时就拒了对象出生即合法。[A003]首尾无空格紧凑构造器里的trim()清洗生效。三个形状的面积78.54 / 24.00 / 9.00各算各的但describe()骨架对它们一视同仁——这就是模板方法骨架固定、细节自由。挑战题答案都在课程源码里⭐照猫画虎照ModernOrder写一个record Student(String id, String name, int score)创建两个学生并打印看 toString 长什么样。⭐加校验在紧凑构造器里加一条——amount大于 100 万也抛异常大额订单走人工审核重新编译验证。⭐⭐删 equals 实验把LegacyOrder手写的 equals 删掉再跑 Main看l1.equals(同值新对象)输出变成什么——想想没写 equals在工程里会踩什么坑提示HashMap 的 key。⭐⭐加形状给 Shape 家族加record Triangle(double base, double height) implements Shape面积 底×高÷2记得更新permits名单重新编译跑通。⭐⭐⭐挑战写一个describeAll(ListShape)内部用 instanceof 链分形状打印不同格式体会家族一扩大if-else 链就要加一个 else-if的痛——这就是下节课模式匹配 switch 要解决的问题。面试回答模板问record 是什么解决了什么问题一句话结论record 是 Java 16 正式发布的数据类语法糖声明一行字段就自动生成构造器、equals、hashCode、toString 和访问器。展开 1~2 句原理/例子字段都是 final对象天然不可变equals 逐字段自动比较从根上消灭了加字段忘了改 equals这类 bug还能在紧凑构造器里做参数校验非法对象根本建不出来。收一句应用场景日常写 DTO、订单、坐标这种纯数据载体时我都用 record代码量从五六十行缩到一行。预判追问“record 能继承吗”——“不能。它隐式 extendsjava.lang.RecordJava 单继承被占了所以既不能 extends 别人也不能被 extends”。追问sealed 呢——“sealed 配 permits 圈定谁能继承我被允许的子类必须是 final / sealed / non-sealed 三选一它让类型家族封闭是模式匹配 switch 省略 default 的前提”。总结表坑 → 原因 → 解法坑原因解法加字段忘了改 equals订单去重失效样板代码手写字段一变就失同步数据载体用recordequals 自动逐字段比较record 编译报错JDK 不是 21record 要 16sealed 要 17java -version确认 21r1.getItem()报错record 访问器是item()不是 getter 风格用r1.item()负数金额订单建出来了没在创建时校验紧凑构造器里抛异常对象出生即合法sealed 的 permits 名单编译报错名单类必须和接口同包且必须真实实现确认同包 名单上每个类都 implements想给 record 加字段record 字段在声明括号里写死只能改声明它只适合纯数据载体关于这个系列本文属于Java 后端实战精通营系列Java 21 实战 10 课。完整代码在java21-journey 课程第 4 课lesson-04/src/main/java/com/java21/lesson04/含LegacyOrder.java传统 POJO、ModernOrder.javarecord、Shape.java密封接口 三个形状gitclone https://gitee.com/j67mk2/java21-journey.gitcdjava21-journey/lesson-04 mvn-qcleaninstall-DskipTestsjava-cptarget/classes com.java21.lesson04.Main仓库地址是小写 j 的 j67mk2复制别丢字母。跑完会看到传统 POJO 和 record 的打印对比、负数订单被拦、三个形状面积。下一篇预告模式匹配 switch——switch 从等值判断升级成类型匹配配合 sealed 的穷尽性default都能省掉。掏心窝一句record 这个特性学的时候觉得就是少写几行但它是消灭一整类 bugequals/hashCode 失同步的语法。面试官问Java 21 有什么新特性你答 record 紧凑构造器 sealed 穷尽性这一套明显比背名字高一个段位。跑出输出和我不一样评论区一起查。【发布信息】主标题写个订单类要 55 行Java 21 的 record 一行搞定备选标题 2sealed 密封类Java 21 让谁能继承我由你自己说了算备选标题 3从 55 行 POJO 到一行 recordJava 21 把数据类的样板代码全删了含负数金额拦截标签建议java / Java21 / record摘要建议手写订单类五六十行样板代码加字段忘了改 equals 直接出生产事故。Java 21 的 record 一行声明搞定构造器/equals/hashCode/toString紧凑构造器还能在对象出生时拦住负数金额搭配 sealed 密封类实现类型封闭与穷尽性。附完整代码、实测输出与面试回答模板。
阅读完成 · 觉得有帮助?
咨询建站