Lombok @builder 在扩展另一个类的类上

Posted

技术标签:

【中文标题】Lombok @builder 在扩展另一个类的类上【英文标题】:Lombok @builder on a class that extends another class 【发布时间】:2017-12-10 10:59:44 【问题描述】:

我有两个课程 Child 扩展 Parent。我需要在类上添加@Builder 注释,这样我就不需要自己创建构建器了。

package jerry;// Internal compiler error: java.lang.NullPointerException

import lombok.AllArgsConstructor;
import lombok.Builder;

@AllArgsConstructor(onConstructor=@__(@Builder))
public class Child extends Parent  
//Multiple markers at this line
//  - Implicit super constructor Parent() is undefined. Must explicitly invoke another constructor
//  - overrides java.lang.Object.toString

   private String a;
   private int b;
   private boolean c;




@Builder
public class Parent 
    private double d;
    private float e;

我需要能够构建子实例,这样

Child child = Child.builder().a("aVal").b(1000).c(true).d(10.1).e(20.0F).build();

但到目前为止,我收到了代码 cmets 中提到的错误。谁能指出我正确的方向如何使用 lombok 或任何其他类似的库来实现它?

子问题

为什么@AllArgsConstructor(onConstructor=@__(@Autowired)) 可以编译而@AllArgsConstructor(onConstructor=@__(@Builder)) 不能编译?

【问题讨论】:

我目前正在尝试评估github.com/rzwitserloot/lombok/pull/1337/commits。 【参考方案1】:

自 1.18.2 版以来,lombok 包含 the new experimental @SuperBuilder。它支持来自超类(也包括抽象类)的字段。有了它,解决方法就这么简单:

@SuperBuilder
public class Child extends Parent 
   private String a;
   private int b;
   private boolean c;


@SuperBuilder
public class Parent 
    private double d;
    private float e;


Child instance = Child.builder().b(7).e(6.3).build();

2019 年 10 月 9 日更新:如果您使用 IntelliJ,则至少需要 0.27 版 IntelliJ Lombok 插件才能使用@SuperBuilder

PS:@AllArgsConstructor(onConstructor=@__(@Builder)) 不起作用,因为@Builder 是一个注解处理注解,lombok 在编译期间会转换为代码。生成然后翻译新的 lombok 注释需要多次迭代注释处理,而 lombok 不支持。相比之下,@Autowired 是运行时可用的常规 Java 注释。

【讨论】:

只是一个警告:IntelliJ Lombok 插件尚不支持。 很好,我会检查的 IntelliJ Lombok 插件的当前版本 0.27 现在支持@SuperBuilder 除非升级到 1.18.16 github.com/rzwitserloot/lombok/issues/2359,否则无法使用泛型类型扩展超类【参考方案2】:

参见https://blog.codecentric.de/en/2016/05/reducing-boilerplate-code-project-lombok/(@Builder 和继承部分)

根据您的课程调整

@AllArgsConstructor
public class Parent 
  private double d;
  private float e;


public class Child extends Parent 
  private String a;
  private int b;
  private boolean c;

  @Builder
  public Child(String a, int b, boolean c, double d, float e) 
    super(d, e);
    this.a = a;
    this.b = b;
    this.c = c;
  

有了这个设置

Child child = Child.builder().a("aVal").b(1000).c(true).d(10.1).e(20.0F).build();

正常工作

【讨论】:

好点我确实尝试过......但是我不能为任何函数提供超过 7 个参数,这是我团队中的代码约定,如果我提供超过 7 个参数,则构建失败。我的原始代码在超/父类中有30多个字段 不列出子构造函数/@Builder中所有的父+子字段不知道能不能做到,至少我没有找到这样的例子。作为一种解决方法,您可能会考虑为这个特定的类禁止这种代码约定(至少我认为它来自一些静态代码分析工具,如 PMD、Findbugs、Sonar 等可以被禁止) 如果父类也有@Builder注解就不行 @ShrikantPrabhu 在那种情况下,我现在在哪里,我只是中止继承并复制字段;幸运的是只有几个领域是共同的。 当您从无法修改的父类扩展时,此解决方案也很有效!!!!!!谢谢【参考方案3】:

您需要在每个对象中使用@SuperBuilder(toBuilder = true)

@Data
@SuperBuilder(toBuilder = true)
public class Parent extends Child 


@Data
@SuperBuilder(toBuilder = true)
public class Child 

【讨论】:

谢谢,我会检查【参考方案4】:

我有一个类似但略有不同的用例。就我而言,我有一个构建和执行请求的抽象超类链。不同的请求共享一些公共参数,但并非每个请求都支持所有参数。具体请求的构建器应仅提供为给定请求设置支持参数的方法。

我开始使用基于构造函数的构建器实现,但这会导致样板代码过多,尤其是在您需要配置许多字段的情况下。我现在想出了以下解决方案,对我来说看起来更干净。

基本上,具体的子类定义了 Builder 应该支持的实际字段,而 Getter 注释导致相应的超类方法被覆盖。抽象超类中的 getter 甚至可以定义为抽象,以需要具体实现来定义这些字段。

public abstract class AbstractSuperClass1 
    protected String getParamA()  return "defaultValueA"; 

    public final void doSomething() 
        System.out.println(getParamA());
        doSomeThingElse();
    

    protected abstract void doSomeThingElse();


public abstract class AbstractSuperClass2 extends AbstractSuperClass1 
    protected String getParamB()  return "defaultValueB"; 

    protected void doSomeThingElse() 
        System.out.println(getParamB());
    


import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;

@Getter(AccessLevel.PROTECTED) @Builder
public class ConcreteClass1 extends AbstractSuperClass2 
    private final String paramA;
    // Not supported by this implementation: private final String paramB;

    public static void main(String[] args) 
        ConcreteClass1.builder()
           .paramA("NonDefaultValueA")
           .build().doSomething();
    


import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;

@Getter(AccessLevel.PROTECTED) @Builder
public class ConcreteClass2 extends AbstractSuperClass2 
    private final String paramA;
    private final String paramB;

    public static void main(String[] args) 
        ConcreteClass2.builder()
            .paramA("NonDefaultValueA").paramB("NonDefaultValueB")
            .build().doSomething();
    

【讨论】:

【参考方案5】:

如果你的子类想要在父类已经有builder方法的情况下,那么你可以为子类创建一个all args构造函数,然后在子类的构造函数上注释builder并设置属性

@Builder(builderMethodName = "custombuildername")

然后在创建子类 Object 时调用 customname builder。

package jerry;

import lombok.AllArgsConstructor;
import lombok.Builder;

@AllArgsConstructor(onConstructor=@__(@Builder))
public class Child extends Parent  

   private String a;
   private int b;
   private boolean c;

   @Builder(builderMethodName = "childBuilder")
   public Child(String a, int b, boolean c, double d, float e)
       super(d,e);
   




@Builder
public class Parent 
    private double d;
    private float e;

【讨论】:

以上是关于Lombok @builder 在扩展另一个类的类上的主要内容,如果未能解决你的问题,请参考以下文章

还在手写 Builder 模式?试试 Lombok 中的 @Builder 用法,太强了!

还在手写 Builder 模式?试试 Lombok 中的 @Builder 用法,太强了。。

还在手写 Builder 模式?试试 Lombok 中的 @Builder 用法,太强了。。

如何从 Lombok builder 中排除财产?

PMD 错误:在 Lombok Builder 中覆盖某些方法时的 AccessorMethodGeneration

java Builder 类中的附加方法(lombok 注释)