Friday Repost: Groovy’s @Immutable pitfalls

The Friday Repost series are copies from my earlier writings. Since I don’t want to loose them, and they might prove useful to others, I’m reposting them on this blog.

Groovy 1.6 introduced some Groovy AST transformations like @Lazy, @Delegate and @Immutable, and there are more. This blog describes some pitfalls of using @Immutable, but to do that, a proper example of @Immutable seems well deserved.

A first introduction

@Immutable, like the name suggests, makes your classes immutable. This means that state changes from the outside, for example by the use of setters, are no longer possible. A good use case for this is when building Commands or DTO’s (also known as Command Query Separation). See for example the following code:

The state of this class should not change. However, in Groovy, this is a bit harder to do than in Java, since Groovy uses a lot of reflection to invoke properties. So the following is quite valid in Groovy:

Being able to do so could cause a lot of unexpected things, and a lot of unwanted behavior! To make sure this doesn’t happen, we can use the @Immutable annotation:

As you can see, two things have changed:

  1. The class is now annotated with @Immutable
  2. The class has been made final, which is a requirement for @Immutable

      When executing the above code again, an exception will be thrown:

      groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: married for class: CreateCustomerCommand

      Seems all pretty and cute, right? However, what would happen when someone with evil intentions (or maybe someone with an old Java habbit), decides to create a setter for this class?

      Now, the code does execute without throwing an exception, and suddenly the code is mutable again. Unfortunately, there’s not much we can do about this, but it breaks the @Immutable contract. There is a small note on this on the @Immutable documentation page:

      “You don’t have to follow Groovy’s normal property conventions, e.g. you can create an explicit private field and then you can write explicit get and set methods. Such an approach, isn’t currently prohibited (to give you some wiggle room to get around these conventions) but any fields created in this way are deemed not to be part of the significant state of the object and aren’t factored into the equals or hashCode methods. Use at your own risk!”

      This text describes the other features of @Immutable: an automatic equals and hashCode method. Since the setter breaks the @Immutable behavior, this is excluded from the equals / hashCode generation. However, this only applies when the fields are private! So be aware of that!


      My conclusion in this is quite simple: don’t expose state changes to the outside world whenever your intention is to be @Immutable!

      Happy coding!

1 reply

Trackbacks & Pingbacks

Comments are closed.