我想创建一个新的
Child
实例传递一个Parent
和其他附加参数。
例如如果我有:
public class Parent {
public String param1;
public String param2;
// many parameters
public String paramN;
}
public class Child extends Parent {
public String subValue;
}
使用 lombok,是否有一个构建器可以让我创建一个
Child
实例,将 Parent
和缺失值作为参数传递?
如果我能写这样的话会更容易:
Parent p = Parent.builder()
.param1("a")
.param2("b")
// many parameters
.paramN("b")
.build();
Child c = Child.builder(p).subValue("c").build();
常规的
@Builder
在这里是不够的,因为你正在处理类层次结构。然而,@SuperBuilder
正是为这种情况而制作的。
@SuperBuilder
生成加载了泛型的复杂代码。如果不深入了解 @SuperBuilder
生成的代码,这使得该解决方案难以理解。你应该考虑一下这是否值得。
这是解决方案(Lombok >= 1.18.16):
@SuperBuilder(toBuilder = true)
public static class Parent {
public String param1;
public String param2;
// many parameters
public String paramN;
public abstract static class ParentBuilder<C extends Parent, B extends Parent.ParentBuilder<C, B>> {
protected B $fillValuesFromParent(Parent instance) {
$fillValuesFromInstanceIntoBuilder(instance, this);
return self();
}
}
}
@SuperBuilder(toBuilder = true)
public static class Child extends Parent {
public String subValue;
public static ChildBuilder<?, ?> toBuilder(Parent p) {
return new ChildBuilderImpl().$fillValuesFromParent(p);
}
}
toBuilder
上的新Child
方法创建了一个新的ChildBuilderImpl
(调用Child
时将创建一个build()
实例)。为了从给定的 Parent p
中填充值,它从 $fillValuesFromParent
调用新的 ParentBuilder
方法。此方法进一步委托对方法$fillValuesFromInstanceIntoBuilder
的调用,该方法由 Lombok 生成并执行将字段值实际复制到新构建器实例。
还要注意方法上的
$
前缀。这基本上是说:我是一个实现细节;除非你知道自己在做什么,否则不要使用我,我可能会在下一个 Lombok 版本上中断,恕不另行通知。
其他答案并不能真正使您的客户端代码简单地重用您已有的父实例。但这是可行的。你有两个选择:
困难的是编写您想要的自定义注释。您甚至可以使其通用,以便它适用于具有父/子层次结构的任何类。看看这个example。如果您有勇气,可以在 Lombok 的 github 页面上提出功能请求。
选项二是为孩子编写自定义生成器。请参阅此处的示例。在初始化步骤的自定义构建器中,您将读取传入的父实例,并仅设置继承的字段。
另一种可能对关注此线程的人有用的方法是利用像 MapperStruct 这样的映射库。使用 MapperStruct,您可以定义一个映射器接口,如下所示:
@Mapper
public interface PetMapper {
Dog toDog(Pet pet);
}
定义映射器后,您可以像这样在代码中使用它:
private static final PetMapper mapper;
...
public void myFunction(Pet pet) {
mapper.toDog(pet).toBuilder().id("My ID").build();
}
或
private static final PetMapper mapper;
...
public void myFunction(Pet pet) {
mapper.toDog(pet).setId("My ID");
}
这样,您可以轻松地在不同对象之间进行映射,并根据需要修改生成的对象。
这里的想法是让映射器处理从父类(或其兄弟类)创建新对象子对象,然后使用该映射对象,您可以使用其设置器来设置自定义属性,或者您可以使用 toBuilder( )(请注意,调用 .build() 后 toBuilder 将创建一个新对象)。