Paul King created GROOVY-10955: ---------------------------------- Summary: @Builder doesn't work on records Key: GROOVY-10955 URL: https://issues.apache.org/jira/browse/GROOVY-10955 Project: Groovy Issue Type: Bug Reporter: Paul King
For this code: {code} import groovy.transform.builder.* @Builder record Developer(Integer id, String first, String last, String email, List<String> skills) { } Developer.builder().id(2).build() {code} The code fails in the {{build}} method. It is meant to create a new Developer but instead creates a DeveloperBuilder instance and then throws a cast exception: {noformat} org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'Developer$DeveloperBuilder@5ef26266' with class 'Developer$DeveloperBuilder' to class 'Developer' ... at Developer$DeveloperBuilder.build(ConsoleScript25) {noformat} I wasn't necessarily expecting it to work. It could be made to work or we could explicitly disable it for records. Similarly, this code fails: {code} @Builder(builderStrategy=InitializerStrategy) record Developer(Integer id, String first, String last, String email, List<String> skills) { } Developer.createInitializer().id(2).build() {code} with this more obscure error: {noformat} java.lang.ArrayIndexOutOfBoundsException: Internal compiler error while compiling ConsoleScript26 Method: org.codehaus.groovy.ast.MethodNode@7cd420f9[Developer$DeveloperInitializer id(java.lang.Integer) from Developer$DeveloperInitializer] Line -1, expecting casting to Developer$DeveloperInitializer<groovy.transform.builder.InitializerStrategy$SET, T1, T2, T3, T4> but operand stack is empty ... at org.codehaus.groovy.classgen.asm.OperandStack.doConvertAndCast(OperandStack.java:340) at org.codehaus.groovy.classgen.asm.StatementWriter.writeReturn(StatementWriter.java:593) at org.codehaus.groovy.classgen.AsmClassGenerator.visitReturnStatement(AsmClassGenerator.java:822) ... {noformat} I would probably just used the named args style rather than a builder, e.g.: {code} var dev1 = new Developer(id: 1, first: 'Dan', last: 'Vega', email: 'danv...@gmail.com', skills: ['Java', 'Spring']) assert dev1.with{ [id, first, last, email, skills] } == // [1, 'Dan', 'Vega', 'danv...@gmail.com', ['Java', 'Spring']] {code} But we should either support or disable one or more of the @Builder strategies. Builder can also be written on constructors. That does work for the default strategy but again not for the InitializerStrategy. Here is a working example: {code} import groovy.transform.builder.* record Developer(Integer id, String first, String last, String email, List<String> skills) { @Builder Developer(Integer id, String full, String email, List<String> skills) { this(id, full.split(' ')[0], full.split(' ')[1], email, skills) } } var dev1 = new Developer(id: 1, first: 'Dan', last: 'Vega', email: 'danv...@gmail.com', skills: ['Java', 'Spring']) assert dev1.with{ [id, first, last, email, skills] } == [1, 'Dan', 'Vega', 'danv...@gmail.com', ['Java', 'Spring']] var dev2 = Developer.builder().id(2).full('Paul King').email('pa...@apache.org').skills(['Java', 'Groovy']).build() assert dev2.with{ [id, first, last, email, skills] } == [2, 'Paul', 'King', 'pa...@apache.org', ['Java', 'Groovy']] {code} -- This message was sent by Atlassian Jira (v8.20.10#820010)