Class annotation used to assist in the creation of mutable classes.
It allows you to write classes in this shortened form:
@Canonical
class Customer {
String first, last
int age
Date since
Collection favItems = ['Food']
def object
}
def d = new Date()
def anyObject = new Object()
def c1 = new Customer(first:'Tom', last:'Jones', age:21, since:d, favItems:['Books', 'Games'], object: anyObject)
def c2 = new Customer('Tom', 'Jones', 21, d, ['Books', 'Games'], anyObject)
assert c1 == c2
If you set the autoDefaults flag to true, you don't need to provide all arguments in constructors calls,
in this case all properties not present are initialized to the default value:
def c3 = new Customer(last: 'Jones', age: 21)
def c4 = new Customer('Tom', 'Jones')
assert null == c3.since
assert 0 == c4.age
assert c3.favItems == ['Food'] && c4.favItems == ['Food']
The
@Canonical
annotation instructs the compiler to execute an
AST transformation which adds positional constructors,
equals, hashCode and a pretty print toString to your class. There are additional
annotations if you only need some of the functionality:
@EqualsAndHashCode
,
@ToString
and
@TupleConstructor
. In addition, you can add one of
the other annotations if you need to further customize the behavior of the
AST transformation.
A class created in this way has the following characteristics:
- A no-arg constructor is provided which allows you to set properties by name using Groovy's normal bean conventions.
- Tuple-style constructors are provided which allow you to set properties in the same order as they are defined.
- Default
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.
Limitations:
-
If you explicitly add your own constructors, then the transformation will not add any other constructor to the class.