Package groovy.transform
Annotation Type Immutable
Class annotation used to assist in the creation of immutable classes.
It allows you to write classes in this shortened form:
@Immutable
class Customer {
String first, last
int age
Date since
Collection favItems
}
def d = new Date()
def c1 = new Customer(first:'Tom', last:'Jones', age:21, since:d, favItems:['Books', 'Games'])
def c2 = new Customer('Tom', 'Jones', 21, d, ['Books', 'Games'])
assert c1 == c2
The @Immutable
annotation instructs the compiler to execute an
AST transformation which adds the necessary getters, constructors,
equals, hashCode and other helper methods that are typically written
when creating immutable classes with the defined properties.
A class created in this way has the following characteristics:
- The class is automatically made final.
- Properties must be of an immutable type or a type with a strategy for handling non-immutable
characteristics. Specifically, the type must be one of the primitive or wrapper types, Strings, enums,
other
@Immutable
classes or known immutables (e.g. java.awt.Color, java.net.URI, java.util.UUID). Also handled are Cloneable classes, collections, maps and arrays, and other "effectively immutable" classes with special handling (e.g. java.util.Date). - Properties automatically have private, final backing fields with getters.
Attempts to update the property will result in a
ReadOnlyPropertyException
. - A map-based constructor is provided which allows you to set properties by name.
- A tuple-style constructor is provided which allows you to set properties in the same order as they are defined.
- Default
equals
,hashCode
andtoString
methods are provided based on the property values. Though not normally required, you may write your own implementations of these methods. Forequals
andhashCode
, if you do write your own method, it is up to you to obey the general contract forequals
methods and supply a corresponding matchinghashCode
method. If you do provide one of these methods explicitly, the default implementation will be made available in a private "underscore" variant which you can call. E.g., you could provide a (not very elegant) multi-line formattedtoString
method forCustomer
above as follows:String toString() { _toString().replaceAll(/\(/, '(\n\t').replaceAll(/\)/, '\n)').replaceAll(/, /, '\n\t') }
If an "underscore" version of the respective method already exists, then no default implementation is provided. Date
s,Cloneable
s and arrays are defensively copied on the way in (constructor) and out (getters). Arrays andCloneable
objects use theclone
method. For your own classes, it is up to you to define this method and use deep cloning if appropriate.Collection
s andMap
s are wrapped by immutable wrapper classes (but not deeply cloned!). Attempts to update them will result in anUnsupportedOperationException
.- Fields that are enums or other
@Immutable
classes are allowed but for an otherwise possible mutable property type, an error is thrown. - 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
orhashCode
methods. Similarly, you may use static properties (though usually this is discouraged) and these too will be ignored as far as significant state is concerned. If you do break standard conventions, you do so at your own risk and your objects may no longer be immutable. It is up to you to ensure that your objects remain immutable at least to the extent expected in other parts of your program!
@Canonical
.
Customising behaviour:
You can customise the toString() method provided for you by @Immutable
by also adding the @ToString
annotation to your class definition.
Limitations:
-
As outlined above, Arrays and
Cloneable
objects use theclone
method. For your own classes, it is up to you to define this method and use deep cloning if appropriate. -
As outlined above,
Collection
s andMap
s are wrapped by immutable wrapper classes (but not deeply cloned!). -
Currently
BigInteger
andBigDecimal
are deemed immutable but see: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6348370 -
java.awt.Color
is treated as "effectively immutable" but is not final so while not normally used with child classes, it isn't strictly immutable. Use at your own risk. -
java.util.Date
is treated as "effectively immutable" but is not final so it isn't strictly immutable. Use at your own risk.
-
Optional Element Summary
Optional ElementsModifier and TypeOptional ElementDescriptionClass[]
Allows you to provide@Immutable
with a list of classes which are deemed immutable.
-
Element Details
-
knownImmutableClasses
Class[] knownImmutableClassesAllows you to provide@Immutable
with a list of classes which are deemed immutable. By supplying a class in this list, you are vouching for its immutability and@Immutable
will do no further checks. Example:import groovy.transform.*
@Immutable
(knownImmutableClasses = [Address]) class Person { String first, last Address address }@TupleConstructor
class Address { final String street }- Since:
- 1.8.7
- Default:
- {}
-