@Documented @Retention(value=SOURCE) @Target(value=TYPE) public @interface Canonical
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, e.g.:
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:
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.
If you want similar functionality to what this annotation provides but also require immutability, see the
@
Immutable
annotation.
Limitations:
LinkedHashMap
or if there is a single Map, AbstractMap or HashMap propertyEqualsAndHashCode
,
ToString
,
TupleConstructor
,
Immutable
public abstract String[] excludes
@Canonical
behavior is customised by using it in conjunction with one of the more specific
related annotations (i.e. @ToString
, @EqualsAndHashCode
or @TupleConstructor
), then
the value of this attribute can be overriden within the more specific annotation.public abstract String[] includes
@Canonical
behavior is customised by using it in conjunction with one of the more specific
related annotations (i.e. @ToString
, @EqualsAndHashCode
or @TupleConstructor
), then
the value of this attribute can be overriden within the more specific annotation.