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:
class CreateCustomerCommand { String name Date birthDate boolean married }
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:
def command = new CreateCustomerCommand(name:'Erik Pragt', married:false) command.married = true
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:
@Immutable final class CreateCustomerCommand { String name Date birthDate boolean married }
As you can see, two things have changed:
- The class is now annotated with @Immutable
- 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?
@Immutable final class CreateCustomerCommand { String name Date birthDate boolean married void setMarried(boolean married) { this.married = married } }
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!
Conclusion
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!
The post Friday Repost: Groovy’s @Immutable pitfalls appeared first on Jworks.