groovy @Builder AST中的默认值

时间:2016-01-28 16:23:54

标签: groovy

我是groovy的新手,我只是想在这里学习。我有一个简单的pojo如下,我想在这里有一个建设者模式。现在,该类使用@Builder annotaion进行注释。

@Builder
class Invert {
      String color = 'Green'
      String code
}

在我的主类中,如果创建一个Invert类的对象,变量颜色总是为空。

Invert in = new Invert.builder().code('0000').build()

创建对象时代码为0000但颜色为null。是否可以预料到,如果有这样的解决方法,或者我在这里遗漏了什么?

2 个答案:

答案 0 :(得分:2)

首先,让我告诉你发生了什么。我将从Invert类开始:

@groovy.transform.builder.Builder
class Invert {
      String color = 'Green'
      String code
}

这就是你的例子中的同一个类,唯一的区别是@Builder的完全限定名。但是看看Groovy编译代码时会发生什么(这是Groovy控制台中AST查看器的相关代码)。

反转类

@groovy.transform.builder.Builder
public class Invert implements groovy.lang.GroovyObject extends java.lang.Object { 

    private java.lang.String color 
    private java.lang.String code 
    ...

    public Invert() {
        color = 'Green'
        metaClass = /*BytecodeExpression*/
    }

    public static Invert$InvertBuilder builder() {
        return new Invert$InvertBuilder()
    }

    ...
}

没什么好吃的,但请注意构造函数中color设置为green

反转$ InvertBuilder类

public static class Invert$InvertBuilder implements groovy.lang.GroovyObject extends java.lang.Object { 

    private java.lang.String color 
    private java.lang.String code 
    ...

    public Invert$InvertBuilder color(java.lang.String color) {
        this .color = color 
        return this 
    }

    public Invert$InvertBuilder code(java.lang.String code) {
        this .code = code 
        return this 
    }

    public Invert build() {
        Invert _theInvert = new Invert()
        _theInvert .color = color 
        _theInvert .code = code 
        return _theInvert 
    }    
    ...

}

Invert$InvertBuilder类由@Builder AST创建。它提供了流畅的API。

问题

您是否抓住了问题的根源?构建器包含自己的colorcode字段。然后,当build()被调用时:

  1. 创建了Invert的实例。此时,实例的colorgreen
  2. 构建器将自己的colorcode字段应用于Invert实例。此时,color更改为null,因为这是构建器中的默认值。
  3. 解决方案

    要使用构建器解决此问题,请使用ExternalStrategy定义您自己的构建器类,您可以在其中根据需要设置color。这是一个有效的例子:

    class Invert {
          String color
          String code
    
          static InvertBuilder builder() {
              new InvertBuilder()
          }
    }
    
    @groovy.transform.builder.Builder(builderStrategy=groovy.transform.builder.ExternalStrategy, forClass=Invert)
    class InvertBuilder {
        InvertBuilder() {
            color = 'Green'
        }
    }
    
    Invert invert = Invert.builder().code('0000').build()
    
    assert invert.color == 'Green'
    assert invert.code == '0000'
    

    替代

    Groovy提供了一些您可能感兴趣的替代构建方法。

    Invert invert1 = new Invert(code: '0000', color: 'Blue') // Using the Map-based constructor
    
    Invert invert2 = new Invert().with { // using Object.with(Closure)
        code = '0000'
        color = 'Blue'
    
        return delegate
    }
    

答案 1 :(得分:0)

Builder API要求您使用构建器语法初始化每个属性。 未指定“颜色”的值。属性,您实际上默默地指定该值应为null,从而覆盖您的' Green'默认值。

您可以将代码调整为以下内容,这是我运行测试的简单groovy脚本:

import groovy.transform.builder.*
import groovy.transform.*

@Canonical
@Builder
class Invert {
    String color
    String code
}

def invert = new Invert().builder().code('000').color('Green').build()
println invert

不是我使用Builder语法显式设置颜色属性,结果为:

  

反转(绿色,000)

另请注意使用的@Canonical注释。这是一个很好的注释,结合了几个注释:

@EqualsAndHashCode,@ ToTtring和@TupleConstructor

除了提供一个很好的toString()输出,你可以在运行脚本的输出中看到,TupleConstructor(在我看来)比使用@Builder类型模式更可取。

使用@TupleConstructor,您可以像这样简单地构造您的Invert对象:

def invert = new Invert(code:'000', color:'Green')

此外,TupleConstructor允许您保留为颜色设置的默认值,如果您未在构造函数中设置它,则不会覆盖它:

import groovy.transform.*

@Canonical
class Invert {
    String color = 'Green'
    String code
}

def invert = new Invert(code:'000')
println invert

结果:

  

反转(绿色,000)

所以,如果是我,我会删除@Builder并使用@Canonical作为最佳实践。 有关@Canonical的更多信息,请参阅:

http://docs.groovy-lang.org/next/html/gapi/groovy/transform/Canonical.html