基于Lombok @SuperBuilder实现子类对象转换并保留父类字段
问题解答
Lombok原生支持情况
Lombok的@SuperBuilder目前不支持直接通过父类Builder跨子类传递状态,哪怕父类是抽象类也不行。原因是每个子类的Builder都是Lombok自动生成的专属实现:ChildA.toBuilder()返回的是ChildA.ChildABuilder(继承自Parent.ParentBuilder),但ChildB的Builder并没有提供方法来接收并拷贝这个ParentBuilder里的字段值——Lombok没有为跨子类的属性拷贝生成对应的逻辑。
可行解决方案
方案1:反射工具类(适配父类字段增删)
写一个通用工具类,通过反射读取父类的所有字段,将源实例的父类字段值拷贝到目标子类的Builder中。这种方式不需要修改现有类结构,且父类字段增删时自动适配:
import java.lang.reflect.Field; import java.lang.reflect.Method; public class ParentFieldCopier { // 拷贝父类字段到目标Builder @SuppressWarnings("unchecked") public static <B> B copyParentFields(Parent source, B targetBuilder) { Field[] parentFields = Parent.class.getDeclaredFields(); for (Field field : parentFields) { field.setAccessible(true); try { // 拼接Builder的setter方法名,比如parentProp -> setParentProp String setterName = "set" + capitalize(field.getName()); Method setter = targetBuilder.getClass().getMethod(setterName, field.getType()); setter.invoke(targetBuilder, field.get(source)); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException("Failed to copy parent field: " + field.getName(), e); } } return targetBuilder; } // 首字母大写工具方法 private static String capitalize(String name) { if (name == null || name.isEmpty()) return name; return name.substring(0, 1).toUpperCase() + name.substring(1); } }
使用方式:
ChildA childA = ChildA.builder().parentProp("parentValue").childAProp("childA").build(); ChildB childB = ParentFieldCopier.copyParentFields(childA, ChildB.builder()) .childBProp("childB") .build();
方案2:自定义父类Builder方法(类型安全但需同步字段)
如果父类字段变动不频繁,可以在父类的Builder中添加自定义方法,让子类Builder继承该方法来拷贝父类字段:
@NonFinal @SuperBuilder(toBuilder = true) public abstract class Parent { String parentProp; // 父类Builder添加从Parent实例拷贝字段的方法 public abstract static class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> { public B fromParent(Parent parent) { this.parentProp = parent.parentProp; return self(); } } }
使用方式:
ChildA childA = ChildA.builder().parentProp("parentValue").childAProp("childA").build(); ChildB childB = ChildB.builder() .fromParent(childA) .childBProp("childB") .build();
⚠️ 注意:这种方式需要在父类新增字段时,手动同步fromParent方法的代码,无法自动适配字段变化。
方案3:第三方库MapStruct(最优推荐)
如果允许引入第三方库,MapStruct是更优的选择:它会在编译期自动生成类型安全的拷贝代码,支持继承结构,父类字段增删时自动更新拷贝逻辑,且无需反射或手动维护代码。
首先添加MapStruct依赖(以Maven为例):
<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.5.5.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.5.5.Final</version> <scope>provided</scope> </dependency>
定义转换器接口:
import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @Mapper public interface ParentChildConverter { ParentChildConverter INSTANCE = Mappers.getMapper(ParentChildConverter.class); // 自动拷贝父类字段,忽略子类独有的childBProp(后续手动设置) @Mapping(target = "childBProp", ignore = true) ChildB convertChildAToChildB(ChildA childA); // 重载方法,直接设置childBProp default ChildB convertChildAToChildB(ChildA childA, String childBProp) { ChildB childB = convertChildAToChildB(childA); childB.setChildBProp(childBProp); return childB; } }
使用方式:
ChildA childA = ChildA.builder().parentProp("parentValue").childAProp("childA").build(); ChildB childB = ParentChildConverter.INSTANCE.convertChildAToChildB(childA, "childB");
方案对比
| 方案 | 自动适配父类字段变化 | 类型安全 | 性能 | 代码维护成本 |
|---|---|---|---|---|
| 反射工具类 | ✅ | ❌ | 一般 | 低(仅维护工具类) |
| 自定义父类Builder方法 | ❌ | ✅ | 优 | 中(字段变化需同步) |
| MapStruct第三方库 | ✅ | ✅ | 优 | 极低(编译期自动生成) |
内容的提问来源于stack exchange,提问作者MK_Dev
相关产品推荐
相关产品推荐

