如何批量转换并赋值Java对象属性,避免手动逐个调用set方法?
解决方案
方案1:JDK原生反射实现(无第三方依赖,实现最简单)
直接通过JDK自带的反射能力遍历两个对象的所有目标字段,统一应用转换逻辑:
import java.lang.reflect.Field; import java.math.BigDecimal; public class SummaryConverter { public static <T> void convertAllNumericFields(T current, T previous) { Field[] fields = current.getClass().getDeclaredFields(); for (Field field : fields) { // 自定义过滤规则:只处理数值类型字段,排除不需要计算的字段(如主键、月份字段等) Class<?> fieldType = field.getType(); if ((fieldType != int.class && fieldType != Integer.class && fieldType != long.class && fieldType != Long.class && fieldType != double.class && fieldType != Double.class && fieldType != BigDecimal.class) || "id".equals(field.getName()) || "month".equals(field.getName())) { continue; } field.setAccessible(true); try { Object currentVal = field.get(current); Object previousVal = field.get(previous); // 调用你原有的自定义转换方法 Object convertedVal = NumUtil.getFormattedValue(currentVal, previousVal); field.set(current, convertedVal); } catch (IllegalAccessException e) { throw new RuntimeException("字段转换失败:" + field.getName(), e); } } } }
改造后的业务代码:
public Summary addSummary(Summary current) { Summary previous = getPreviousSummary(getPreviousMonth()); // 一行完成所有字段转换 SummaryConverter.convertAllNumericFields(current, previous); return current; }
提示:如果Summary类存在继承自父类的字段,需要补充递归遍历父类字段的逻辑,直到遍历到Object类为止,避免父类字段被漏掉。
方案2:Spring BeanWrapper实现(适合已集成Spring的项目,代码更简洁)
如果项目已经依赖Spring框架,可以用Spring提供的BeanWrapper工具,不需要自己处理字段访问、类型适配等基础问题:
import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import java.beans.PropertyDescriptor; public class SummaryConverter { public static void convertWithBeanWrapper(Summary current, Summary previous) { BeanWrapper currentWrapper = new BeanWrapperImpl(current); BeanWrapper previousWrapper = new BeanWrapperImpl(previous); for (PropertyDescriptor pd : currentWrapper.getPropertyDescriptors()) { String fieldName = pd.getName(); // 过滤不需要处理的属性 if ("class".equals(fieldName) || "id".equals(fieldName) || "month".equals(fieldName)) { continue; } // 只处理可读可写的数值类型属性 if (pd.getReadMethod() == null || pd.getWriteMethod() == null || !Number.class.isAssignableFrom(pd.getPropertyType())) { continue; } Object currentVal = currentWrapper.getPropertyValue(fieldName); Object previousVal = previousWrapper.getPropertyValue(fieldName); Object convertedVal = NumUtil.getFormattedValue(currentVal, previousVal); currentWrapper.setPropertyValue(fieldName, convertedVal); } } }
方案3:MapStruct实现(性能最优,编译期校验,适合生产高并发场景)
如果对性能要求高,不想承担反射的运行时开销,可以用MapStruct编译期自动生成转换代码,性能和手动编写set方法几乎一致,同时支持编译期字段合法性校验:
import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Named; import org.mapstruct.TargetProperty; import org.mapstruct.factory.Mappers; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; @Mapper public interface SummaryMapper { SummaryMapper INSTANCE = Mappers.getMapper(SummaryMapper.class); // 单条注解即可适配所有字段,不需要逐个配置映射关系 @org.mapstruct.Mapping(target = ".", source = "current", qualifiedByName = "convertField") Summary convert(Summary current, @Context Summary previous); @Named("convertField") default Object convertField(Summary current, @Context Summary previous, @TargetProperty String fieldName) { BeanWrapper currentWrapper = new BeanWrapperImpl(current); BeanWrapper previousWrapper = new BeanWrapperImpl(previous); return NumUtil.getFormattedValue(currentWrapper.getPropertyValue(fieldName), previousWrapper.getPropertyValue(fieldName)); } }
业务代码使用方式:
public Summary addSummary(Summary current) { Summary previous = getPreviousSummary(getPreviousMonth()); return SummaryMapper.INSTANCE.convert(current, previous); }
内容的提问来源于stack exchange,提问作者Mankind1023
相关产品推荐
相关产品推荐

