portrait

End of Line blog

Thoughts on software development, by Adam Ruka

Jilt 1.7 released!

The interest in Jilt, my Java library for generating Builder pattern classes (which I’ve written about several times in the recent past) shows no signs of slowing down. The latest release, 1.7, includes several new features and bug fixes:

Add @JiltGenerated to all Builders

One annoyance with using tools like Jilt that rely on code generation is that, if you use a tool for measuring code coverage, you have to make sure to exclude all that generated code from the code coverage report, since otherwise the reported numbers will be skewed. While these tools typically provide a way to exclude classes from being included in their reports, it’s still a manual step that users of Jilt have to remember to do. In addition, Jilt did not allow recognizing Builder classes in any way other than based on their name, and Builder classes can have arbitrary names, so any provided exclusion pattern might potentially miss some of them.

For this reason, Jilt 1.7 now adds a new annotation, @JiltGenerated, with retention policy set to CLASS, to all generated Builder classes. This way, code coverage tools can be configured to exclude all classes containing that annotation. JaCoCo, one of the most popular code coverage tools for Java, actually automatically excludes all classes that are annotated with an annotation that contains the word “Generated” anywhere in its name, so you don’t need any additional configuration for the generated Builder classes to be excluded from code coverage when using JaCoCo.

Since @JiltGenerated is not used for anything by Jilt itself, and the Java runtime ignores any annotations present on the class, but not found at runtime, this feature does not require any changes to your Maven or Gradle setup – in particular, there’s no need to add Jilt to the project’s classpath at runtime.

Thanks to Alexandre Navarro for suggesting this feature.

Allow using * in the @Builder‘s className attribute

Jilt has supported creating Builder meta-annotations since version 1.5. However, that version of this feature had one important limitation: when changing the name of the generated Builder class with the className attribute of the @Builder annotation, that only supported providing a constant string for the class name, which is insufficient for meta-annotations (since the provided name of the generated Builder would cause a naming collision if the same meta-annotation was used on multiple classes in the same Java package).

The @BuilderInterfaces annotation has a similar problem with its innerNames attribute, which allows specifying the names of the generated interfaces used by the Staged or Functional Builders. The way that attribute solves this issue is by allowing using the * character as a placeholder that gets substituted by the (capitalized) name of the property that the interface corresponds to, making it more of a pattern than just a simple name.

Starting with Jilt 1.7, the className attribute of the @Builder annotation now supports a similar pattern substitution, where the * character gets replaced by the name of the class that the Builder is being generated for. This makes that attribute usable with meta-annotations, where you can make all generated Builder classes be named according to the provided pattern, for example:

import org.jilt.Builder;

@Builder(className = "*JiltBuilder")
public @interface MyBuilder {
}

Thanks (again) to Alexandre Navarro for starting the discussion that resulted in this feature.

Fix mixing private constructors with toBuilder

Jilt has supported classes with private constructors, and generating toBuilder methods, also since version 1.5. However, using both of these features together didn’t work – it would generate code that wouldn’t compile, as the toBuilder method would try to create a local instance of the Builder class, but since that class is abstract when the constructor of the target class is private, it cannot be instantiated.

The solution to fix this issue is to add an extra parameter to the generated toBuilder method, of the type of the Builder, if the target class has a private constructor. Inside that method, the Builder instance pointed to by that parameter is used in the same way as the locally created Builder instance from the non-private constructor version of toBuilder. This allows hand-writing a toBuilder method on the target class that calls the generated one, and passes a newly created Builder instance to it, which is probably a static nested class that is private, so not available from outside the target class.

For example, using the same User class as in the original article:

import org.jilt.Builder;
import org.jilt.BuilderStyle;
import org.jilt.Opt;

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) {
        // ...
    }

    private static class InnerBuilder extends UserBuilder {
        @Override
        public User build() {
            return new User(email, username, firstName, lastName, displayName);
        }
    }

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

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

Thanks (again) to Alexandre Navarro for reporting this issue.

Summary

So, those are all the changes included in Jilt release 1.7. Let me know in the comments below if you have any feedback on them, or the library in general.