@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface ImmutableBase
Class annotation used to assist in the creation of immutable classes.
Checks on the validity of an immutable class and makes some preliminary changes to the class.
Usually used via the @Immutable meta annotation.
Custom property handling:
@ImmutableBase annotation supports customization using @PropertyOptions
which allows a custom property handler to be defined. This is most typically used behind the scenes
by the @Immutable meta-annotation but you can also define your own handler. If a custom
handler is present, it will determine the code generated for the getters and setters of any property.| Type | Name and Description |
|---|---|
boolean |
copyWithIf true, this adds a method copyWith which takes a Map of
new property values and returns a new instance of the Immutable class with
these values set.
|
If true, this adds a method copyWith which takes a Map of
new property values and returns a new instance of the Immutable class with
these values set.
Example:
@groovy.transform.Immutable(copyWith = true)
class Person {
String first, last
}
def tim = new Person( 'tim', 'yates' )
def alice = tim.copyWith( first:'alice' )
assert tim.first == 'tim'
assert alice.first == 'alice'
Unknown keys in the map are ignored, and if the values would not change
the object, then the original object is returned.
A key may also be a dotted nested path — copyWith
then returns a new instance with that nested value updated, reusing the
untouched branches (identity is preserved transitively):
@groovy.transform.Immutable(copyWith = true) class Address { String city, zip }
@groovy.transform.Immutable(copyWith = true) class Person { String name; Address address }
def p = new Person('Alice', new Address('NYC', '10001'))
def q = p.copyWith('address.city': 'Boston')
assert q.address.city == 'Boston'
assert q.address.zip == '10001'
Every node on a nested path must itself provide copyWith(Map)
(e.g. an @Immutable/@RecordType declared with
copyWith=true); otherwise a clear error is raised.
A transactional block form is also generated, sugar over the map form.
Within it, old is the original (pre-state) object — aligning
with old in @Ensures/@Contract — so values may
be derived from it; prop.modify { } is a shorthand for the
common transform-this-same-field case:
def q = p.copyWith {
name = 'Bob' // plain set
address.city = old.address.city.reverse() // derive from pre-state
loginCount.modify { it + 1 } // single-field shorthand
}
On a navigated path modify is reserved by the block, so use the
old form (x = old.x.modify { ... }) to call a real
same-named method on the value type — or simply when .modify
would read confusingly.
If a method called copyWith that takes a single parameter already
exists in the class, then this setting is ignored, and no method is generated.