Immutables: how to customize the generated classes

In a previous post we’ve seen a brief introduction to the Immutables library and its main features. With a few annotations we can automatically generate immutable classes out-of-the-box. Such immutable classes provide a whole range of useful characteristics that make them the ideal means to represent a resource’s state in an application. In this post we’ll see how to customize the classes generated by Immutables, with special regard to classes’ names and checks on data.

Customize the generated class’ name

As we saw in the previous post the generated immutable class will have the name of the abstract class it extends, with the prefix Immutable.

We may want to have a different name for our generated class. The annotation we need to use is @Value.Style. Two parameters will allow us to determine the generated class name from the annotated class name:

  • typeAbstract determines the prefix and/or suffix to strip off from the annotated abstract class’ name (if any is present)
  • typeImmutable determines the prefix and/or suffix to add to the generated class’ name

In the following example the annotated class’ name is AbstractTelephone. Since typeAbstract has a value of Abstract* the prefix Abstract will be dropped in the generated class name. At the same time, since typeImmutable has a value of *Dto the generated class name will end with Dto. The generated immutable class’ name will then be TelehponeDto.

Please be aware that the default value for typeAbstract is Abstract* and the default value for typeImmutable is Immutable*. They apply even if the sole annotation @Value.Immutable is on the abstract class and @Value.Style is not.

Both typeAbstract and typeImmutable can be used to both remove/add at the same time a prefix and/or a suffix. For example, if we have an abstract class annotated like the following

The generated immutable class name will be MyFooDto.

Generate a static .of() constructor

Another useful parameter of the @Value.Style annotation is allParameters. This parameter takes a boolean value. If it is set to true, the generated immutable class will have a static all-args constructor called of(). This constructor will provide an alternative to the usual builder to instantiate the immutable class. Needless to say, this kind of static constructor is only convenient when the immutable class has no more than 3 or 4 fields, otherwise it will be very cumbersome to use.

The following test cases shows how setting allParameters=true will allow us to instantiate the immutable class both by means of a builder and by means of static of() constructor

Using @Value.Parameter

As an alternative to allParameters=true, we can place the @Value.Parameter annotation on all getters. This will create a static constructor that takes all the annotated fields as parameters. All non-nullable non-optional getters must have the annotation, otherwise the project won’t compile. The following is equivalent to the AbstractTelephone class above:

Auto-generated toString() method

By default the generated immutable class has a toString() method with the pattern Classname{field1=value1, field2=value2}. The peculiarity is that @Nullable and Optional fields, when not present, will be completely omitted from the string. The following unit tests show how this works.

If you want @Nullable and Optional fields to be always included in the toString() method, thus generating a string like Classname{field1=value1, nullableField=null}, you’ll need to provide your own implementation of toString() in the abstract class. The generated immutable class won’t have any auto-generated toString() method and will inherit the one from the abstract class.

Masking fields in toString()

Sometimes there are fields whose value should never be shown in the toString() method because they contain sensitive data, like a password. All we need is placing the @Value.Redacted annotation on the relative getter field in the abstract class. The relative field will be excluded from the toString method. Alternatively, the field can be included in the toString method, but it can be masked with some fixed string using parameter redactedMask of the @Value.Style annotation. The following AbstractUser class shows such an example.

Performing checks on the fields’ values

It’s also possible to enforce some checks on the fields values by placing the @Value.Check annotation on a method with void return type. Such annotated method’s execution takes place right after the class’ instantiation. The purpose of this annotation is to throw an exception if for any reason the object’s state is invalid or inconsistent. In the following example we added a checkState() method to the AbstractUser class, where the username field is validated for pattern and length.

Conclusion

We saw how we can customize the classes generated by Immutables. By using a few annotations we can tweak the generated immutable classes’ names, the toString() method, we can add static constructors, and we can even implement validity checks on the object’s state.

The example project is available for download on GitLab.