为 java 不可变对象实现 Groovy 构建器的最佳方法

The best way to implement Groovy builder for java immutable object

我有一个不可变的 Java 对象,如下所示:

public class Entity {
    private String field1;
    private String field2;

    public Entity(String field1, String field2) {
        this.field1 = field1;
        this.field2 = field2;
    }

    public String getField1() {...}
    public String getField2() {...}
}

我想在我的测试代码中为 Groovy 中编写的 class 添加一个构建器。为此,我尝试使用 @groovy.transform.builder.Builder。 在这里,我无法更改实体 class 作为产品代码中的实体,因此我尝试将构建器与 groovy.transform.builder.ExternalStrategy 策略一起使用。

@Builder(builderStrategy = ExternalStrategy, forClass = Entity)
class EntityBuilder {
}

但我不能将它用于不可变对象。

所以我想知道为不可变 java 对象实施 groovy 构建器的最佳方法是什么?

不是真正的构建器,但您可以将 List 强制转换为您的类型,只要它具有可以将 List 项作为参数的构造函数即可:

def entity = [ 'hi', 'hej' ] as Entity

如果你的类型有一个接受 2 个字符串的构造函数,这就有效。

由于您的类型没有 setter,Groovy 无法猜测哪个参数具有哪个名称,因此无法自动生成构建器。

但是,您可以手动编写一些代码以获得一些不错的代码。

例如:

@Builder
class GroovyEntity {
    String field1
    String field2

    def asType( Class type ) {
        if ( type == Entity ) {
            return new Entity( field1, field2 )
        }
        super.asType( type )
    }
}

你可以这样使用:

Entity entity = GroovyEntity.builder()
            .field1( 'hi' )
            .field2( 'hej' )
            .build() as Entity

快速回答是没有 Groovy 构建器转换可以为您的情况创建构建器。所以你必须手动编码。不能使用构建器转换的原因有两个:

  1. 字段不是 public,因此转换看不到它们,因此不会为它们创建生成器方法。
  2. 生成的 build() 方法期望构建的 class 包含每个字段的无参数构造函数和设置器。

为了说明我的意思,下面是为满足要求的 class 生成的构建器代码片段:

@groovy.transform.builder.Builder(forClass = Person, builderStrategy = groovy.transform.builder.ExternalStrategy)
public class PersonBuilder implements groovy.lang.GroovyObject extends java.lang.Object { 

    private java.lang.String firstName 
    private java.lang.String lastName 
    ...

    public PersonBuilder firstName(java.lang.String firstName) {
        this .firstName = firstName 
        return this 
    }

    public PersonBuilder lastName(java.lang.String lastName) {
        this .lastName = lastName 
        return this 
    }

    public Person build() {
        Person _thePerson = new Person()
        _thePerson .firstName = firstName 
        _thePerson .lastName = lastName 
        return _thePerson 
    }

    ...

}