Java如何高效实现两个类共有成员变量的部分值匹配拷贝
场景说明
现有两个第三方提供的类,分别对应GET接口响应结构、PUT接口请求体结构,业务流程为:调用GET接口拉取资源当前状态,修改字段后通过PUT接口提交更新。
两个类的结构如下,B包含A的全部字段,还额外扩展了自有字段,逻辑上本应存在继承关系,但因是第三方提供的类,无法修改其继承定义:
class A{ int a; int b; int c; } class B{ int a; int b; int c; int d; }
需求为:将B对象中与A共有的字段值自动复制到A对象,无需编写逐行手动赋值的硬编码逻辑,复制完成后修改A的指定字段即可直接调用PUT接口提交:
a.setC(x); // 修改指定字段 serv.put(a);
可选实现方案
以下方案均不需要逐行维护字段赋值逻辑,后续类新增同名字段时会自动适配,维护成本远低于硬编码:
Spring生态项目首选:Spring自带BeanUtils
若项目已集成Spring,直接用框架内置的BeanUtils即可,工具会自动识别两个类中同名、同类型的字段完成赋值,仅需一行代码:Services serv = new Services(); B b = serv.get(); A a = new A(); // 参数顺序:数据源对象、赋值目标对象 BeanUtils.copyProperties(b, a); // 后续业务逻辑 a.setC(x); serv.put(a);注意:Spring的BeanUtils参数顺序为源在前、目标在后,不要和其他工具类的参数顺序混淆,避免赋值失败。
非Spring项目可选:Apache Commons BeanUtils
未集成Spring的项目可以引入Apache Commons BeanUtils依赖实现相同能力,用法类似:// 参数顺序:赋值目标对象、数据源对象 BeanUtils.copyProperties(a, b);注意:该工具参数顺序和Spring版相反,使用时注意传参顺序。
有自定义映射需求选:MapStruct
如果后续存在字段名不一致、字段类型需要转换的场景,推荐使用MapStruct,它会在编译期自动生成和硬编码性能一致的映射代码,不需要手动写赋值逻辑。
首先定义映射接口:@Mapper public interface ModelMapper { ModelMapper INSTANCE = Mappers.getMapper(ModelMapper.class); A convertBToA(B b); }业务代码直接调用即可,同名同类型字段自动映射,特殊字段可以通过注解配置映射规则:
A a = ModelMapper.INSTANCE.convertBToA(b); a.setC(x); serv.put(a);无依赖场景:自定义反射工具方法
如果项目不允许引入额外依赖,可以自行实现一个简单的反射复制工具,核心逻辑为遍历目标类的所有字段,从源对象中匹配同名字段取值后赋值,一次编写即可支持所有同结构类的字段复制。
内容的提问来源于stack exchange,提问作者TalClip

