文章目录
- 一、业务背景:对象赋值的真实场景
- [二、阿里《Java 开发手册》的核心思想](#二、阿里《Java 开发手册》的核心思想)
- 三、阿里【明确推荐】的做法
-
- [1. Builder 模式(字段多时首选)](#1. Builder 模式(字段多时首选))
- [2. 普通 set 方法(允许,但有限制)](#2. 普通 set 方法(允许,但有限制))
- 四、阿里【明确反对】的做法
-
- [1. BeanUtils.copyProperties(强烈不推荐)](#1. BeanUtils.copyProperties(强烈不推荐))
- [2. 超长构造方法(参数地狱)](#2. 超长构造方法(参数地狱))
- [3. 伪链式 set](#3. 伪链式 set)
- 五、阿里规范下的标准推荐组合
- 六、总结
在日常 Java 业务开发中, 如何规范地给对象赋值(set) ,
业务层会纠结:到底是用构造方法、set 方法,还是 BeanUtils?
本文结合 阿里《Java 开发手册》 ,通过一个真实业务场景,系统梳理对象构建的规范做法。
一、业务背景:对象赋值的真实场景
以购物车为例,常见的实体类如下:
java
public class CartInfo implements Serializable {
private static final long serialVersionUID = 1L;
private Long userId;
private Long skuId;
private BigDecimal cartPrice;
private BigDecimal skuPrice;
private Integer skuNum;
private String thumbImg;
private String skuName;
private Integer isChecked;
}
在 Service 层,我们通常需要把接口参数、数据库数据等组装成一个 CartInfo 对象。
问题随之而来:
- 能不能在业务层一次性 set?
- 用 set、构造方法还是 BeanUtils?
- 哪种方式符合阿里规范?
二、阿里《Java 开发手册》的核心思想
在对象构建这件事上,阿里手册的指导思想非常清晰,可以总结为三点:
- 对象构建要清晰、可读、可维护
- 避免隐式、反射式、魔法赋值
- 字段多时,禁止参数地狱
后面的所有规范,都是围绕这三点展开的。
三、阿里【明确推荐】的做法
1. Builder 模式(字段多时首选)
阿里手册明确指出:
当一个类的构造参数较多(通常 ≥ 5 个)时,推荐使用 Builder 模式。
对于 CartInfo 这种字段较多的业务对象,Builder 是最优解。
实体类写法:
java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CartInfo implements Serializable {
private static final long serialVersionUID = 1L;
private Long userId;
private Long skuId;
private BigDecimal cartPrice;
private BigDecimal skuPrice;
private Integer skuNum;
private String thumbImg;
private String skuName;
private Integer isChecked;
}
业务层写法:
java
CartInfo cartInfo = CartInfo.builder()
.userId(userId)
.skuId(skuId)
.cartPrice(cartPrice)
.skuPrice(skuPrice)
.skuNum(skuNum)
.thumbImg(thumbImg)
.skuName(skuName)
.isChecked(1)
.build();
为什么阿里推荐 Builder?
- 不需要记参数顺序
- 可读性强,一眼知道每个值的含义
- 不容易传错参数
- 后期新增字段不影响旧代码
这是阿里系项目和大厂项目中最常见的写法。
2. 普通 set 方法(允许,但有限制)
阿里手册并没有禁止 set 方法,但强调一个前提:
set 只能用于对象的初始化阶段,并且必须集中完成
合规写法示例:
java
CartInfo cartInfo = new CartInfo();
cartInfo.setUserId(userId);
cartInfo.setSkuId(skuId);
cartInfo.setSkuNum(skuNum);
cartInfo.setCartPrice(cartPrice);
cartInfo.setSkuPrice(skuPrice);
cartInfo.setThumbImg(thumbImg);
cartInfo.setSkuName(skuName);
cartInfo.setIsChecked(1);
注意事项:
- 不允许在多个方法、多个分支中零散 set
- 对象一旦初始化完成,不应再随意修改状态
- 否则会导致对象状态不清晰,维护成本极高
四、阿里【明确反对】的做法
1. BeanUtils.copyProperties(强烈不推荐)
阿里手册明确指出:
避免使用 Apache BeanUtils 或 Spring BeanUtils 进行属性拷贝
原因包括:
- 基于反射,性能差
- 字段不匹配时静默失败
- 调试困难,线上问题难排查
- Code Review 基本必被打回
在阿里系和大厂项目中,BeanUtils 往往是"红线"。
2. 超长构造方法(参数地狱)
java
new CartInfo(a, b, c, d, e, f, g, h);
阿里明确反对这种写法:
- 可读性极差
- 极易传错参数
- 后期维护成本极高
3. 伪链式 set
java
cartInfo.setUserId(userId)
.setSkuId(skuId);
除非是标准 Builder,否则这种写法:
- 容易误导调用方
- 破坏 POJO 语义
- 不符合阿里规范
五、阿里规范下的标准推荐组合
实体类:
java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CartInfo implements Serializable {
private static final long serialVersionUID = 1L;
private Long userId;
private Long skuId;
private BigDecimal cartPrice;
private BigDecimal skuPrice;
private Integer skuNum;
private String thumbImg;
private String skuName;
private Integer isChecked;
}
业务层:
java
CartInfo cartInfo = CartInfo.builder()
.userId(userId)
.skuId(skuId)
.skuNum(skuNum)
.cartPrice(cartPrice)
.skuPrice(skuPrice)
.isChecked(1)
.build();
这套写法在阿里系和主流互联网公司中,几乎是通用答案。
六、总结
字段少用构造器,字段多用 Builder;
set 可以,但必须集中;
BeanUtils 禁用。