@AnnotationCollector(value: [ToString, TupleConstructor, EqualsAndHashCode], mode: AnnotationCollectorMode.PREFER_EXPLICIT_MERGED) @interface Canonical
The @Canonical
meta-annotation combines the @EqualsAndHashCode
,
@ToString
and @TupleConstructor
annotations. It is used to assist in
the creation of mutable classes. It instructs the compiler to execute AST transformations
which add positional constructors, equals, hashCode and a pretty print toString to your class.
You can write classes in this shortened form:
import groovy.transform.Canonical
@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
You don't need to provide all arguments in constructor calls. If using named parameters, any property names not
referenced will be given their default value (as per Java's default unless an explicit initialization constant is
provided when defining the property). If using a tuple constructor, parameters are supplied in the order in which
the properties are defined. Supplied parameters fill the tuple from the left. Any parameters missing on the right
are given their 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']If you don't need all of the functionality of
@Canonical
, you can simply directly use one or more of the individual
annotations which @Canonical
aggregates.
In addition, you can use @Canonical
in combination with explicit use one or more of the individual annotations in
cases where you need to further customize the annotation attributes.
Any applicable annotation attributes from @Canonical
missing from the explicit annotation will be merged
but any existing annotation attributes within the explicit annotation take precedence. So, for example in this case here:
The generated@Canonical
(includeNames=true, excludes='c')@
ToString(excludes='a,b') class MyClass { ... }
toString
will include property names and exclude the a
and b
properties.
A class created using @Canonical
has the following characteristics:
equals
, hashCode
and toString
methods are provided based on the property values.
See the GroovyDoc for the individual annotations for more details.
If you want similar functionality to what this annotation provides but also require immutability, see the
@
Immutable annotation.
More examples:
import groovy.transform.* @Canonical 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 theOffice.officeSpace = true 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' @Canonical @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)'