skinny85 / jilt

Java annotation processor library for auto-generating Builder (including Staged Builder) pattern classes
Other
240 stars 12 forks source link

Compilation errors on the generated Builder when the @Builder is on private constructor and the toBuilder() is activated. #29

Open alexandrenavarro opened 2 days ago

alexandrenavarro commented 2 days ago

Just for information, when the @Builder is on private constructor and the toBuilder() is activated, there are a compilation errors because it tries to instance the builder but it is abstract class. The builder() method is also not generated.

I don't have any elegant solution, one solution is passing the name of the concrete in the configuration of the annotation. Think about it if you find a better solution.

Clearly, it is a nice to have fix / feature not a blocking problem.

Thank you for your useful lib and don't hesitate if you have any questions.

skinny85 commented 2 days ago

Thanks for opening the issue @alexandrenavarro.

Thinking about a potential solution, my idea is to add an additional parameter to the static toBuilder() method in the case @Builder was placed on a private method. This new parameter would be of the type of the abstract Builder class, instead of having it created inside the toBuilder() method and stored in a local variable. This should solve the compilation error. And then, in the manually-written class, either write an instance version of toBuilder(), or add a static one to the inner builder class.

So, something like this:

public final class User {
    public final String email, username, firstName, lastName, displayName;

    @Builder(style = BuilderStyle.STAGED, toBuilder = "toBuilder")
    private User(String email, @Opt String username, String firstName,
            String lastName, @Opt String displayName) {
        this.email = email;
        this.username = username == null ? email : username;
        this.firstName = firstName;
        this.lastName = lastName;
        this.displayName = displayName == null
            ? firstName + " " + lastName
            : displayName;
    }

    public final static class InnerBuilder extends UserBuilder {
        private InnerBuilder() {
        }

        @Override
        public User build() {
            return new User(email, username, firstName, lastName, displayName);
        }
    }

    public static UserBuilders.Email builder() {
        return new InnerBuilder();
    }

    public InnerBuilder toBuilder() {
        return UserBuilder.toBuilder(new InnerBuilder(), this);
    }
}

Thoughts on this idea?

alexandrenavarro commented 2 days ago

It seems a good solution.