@ToString(cache: true, includeSuperProperties: true) @EqualsAndHashCode(cache: true) @ImmutableBase @Final @ImmutableOptions @PropertyOptions(propertyHandler: ImmutablePropertyHandler) @TupleConstructor(defaults: false) @MapConstructor(noArg: true, includeSuperProperties: true, includeFields: true) @KnownImmutable @AnnotationCollector(mode: AnnotationCollectorMode.PREFER_EXPLICIT_MERGED) @Retention(value: RetentionPolicy.SOURCE) @Target(value: ElementType.TYPE) @interface Immutable
Meta annotation used when defining immutable classes.
It allows you to write classes in this shortened form:
@groovy.transform.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 meta-annotation corresponds to adding the following annotations:
ToString,
EqualsAndHashCode,
ImmutableBase,
ImmutableOptions,
PropertyOptions,
TupleConstructor,
MapConstructor and
KnownImmutable.
Together these annotations instruct the compiler to execute the necessary transformations to add
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:
@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, other "effectively immutable"
classes with special handling (e.g. java.util.Date), and usages of java.util.Optional where the
contained type is immutable (e.g. Optional<String>).
ReadOnlyPropertyException.
equals, hashCode and toString methods are provided based on the property values.
Though not normally required, you may write your own implementations of these methods. For equals and hashCode,
if you do write your own method, it is up to you to obey the general contract for equals methods and supply
a corresponding matching hashCode 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 formatted
toString method for Customer 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.
Dates, Cloneables and arrays are defensively copied on the way in (constructor) and out (getters).
Arrays and Cloneable objects use the clone method. For your own classes,
it is up to you to define this method and use deep cloning if appropriate.
Collections and Maps are wrapped by immutable wrapper classes (but not deeply cloned!).
Attempts to update them will result in an UnsupportedOperationException.
@Immutable classes are allowed but for an
otherwise possible mutable property type, an error is thrown.
equals or hashCode 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:
Cloneable objects use the clone method. For your own classes,
it is up to you to define this method and use deep cloning if appropriate.
Collections and Maps are wrapped by immutable wrapper classes (but not deeply cloned!).
BigInteger and BigDecimal 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.
LinkedHashMap or if there is a single Map, AbstractMap or HashMap property.
More examples:
--------------------------------------------------------------------------------
import groovy.transform.*
@Immutable(defaults=true, noArg=false)
class Building {
String name
int floors
boolean officeSpace
}
// Constructors are added.
def officeSpace = new Building('Initech office', 1, true)
// toString() added.
assert officeSpace.toString() == 'Building(Initech office, 1, true)'
// Default values are used if constructor
// arguments are not assigned.
def theOffice = new Building('Wernham Hogg Paper Company')
assert theOffice.floors == 0
assert theOffice.officeSpace == false
def anotherOfficeSpace = new Building(name: 'Initech office', floors: 1, officeSpace: true)
// equals() method is added.
assert anotherOfficeSpace == officeSpace
// equals() and hashCode() are added, so duplicate is not in Set.
def offices = [officeSpace, anotherOfficeSpace, theOffice] as Set
assert offices.size() == 2
assert offices.name.join(',') == 'Initech office,Wernham Hogg Paper Company'
@Immutable
@ToString(excludes='age') // Customize one of the transformations.
class Person {
String name
int age
}
def mrhaki = new Person('mrhaki', 37)
assert mrhaki.toString() == 'Person(mrhaki)'
| Type Params | Return Type | Name and description |
|---|---|---|
|
abstract boolean |
copyWith()No longer used directly but instead collected from ImmutableBase. |
|
abstract Class[] |
knownImmutableClasses()No longer used directly but instead collected from ImmutableOptions. |
|
abstract String[] |
knownImmutables()No longer used directly but instead collected from ImmutableOptions. |
No longer used directly but instead collected from ImmutableBase. Remains for legacy handling only.
No longer used directly but instead collected from ImmutableOptions. Remains for legacy handling only.
No longer used directly but instead collected from ImmutableOptions. Remains for legacy handling only.