[FEATURE] address @Builder.Default and explicit constructors weird behaviour.
Problem
Given:
@Builder @NoArgsConstructor @AllArgsConstructor class Example {
int a;
@Builder.Default int x = 10;
public Example(int i) {
this.a = i + 15;
}
}The code compiles, and if I were to invoke new Example(5), then the value for x ends up being.... 0. Because the default expression (here, 10) is moved, and 'only' used by lombok-generated code (so, the generated no-args constructor uses it, as does the builder).
the common case: the field is final
Right now, if the field is final, then the above code example does not compile, because the manually written constructor does not definitely assign x. If a manually written constructor DOES 'definitely assign' it, I don't think we have a problem here. So that's easy; we need not change anything.
the uncommon case: the field is not final
For non-final fields with defaults, we check if manually written constructors exist. If they do, we analyse the default value and check if it is either a literal, or, a 'field reference', such as Integer.MAX_VALUE. If it is, we make the initializing expression of the field an invocation to the default-generating method (because we know for sure this is side effect free, so any double-invoke doesn't matter). If it is not (for example, it is counter++ or LocalDate.now()), we generate a warning. This warning cannot be removed, which effectively means that you can't use lombok at all. Let's hope the combination of manually written constructors + non-constant defaults are rare enough that it doesn't matter. Even if the 'non-constant default' is constant after all. For example, LocalDate.of(2020, 1, 1) is constant, but lombok doesn't know that.
OPEN QUESTION: Should we have a parameter on Builder.Default to say: yeahyeah just generate the invoke, I'm aware this means any attempt to construct this thing will neccessarily resolve the initializer expression once EVEN IF an explicit value is set?
Source: projectlombok/lombok