#2340·lombok

[FEATURE] address @Builder.Default and explicit constructors weird behaviour.

Author: rzwitserlootCreated Jan 17, 2020Updated Sep 1, 2026

Problem

Given:

java
    @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?