Using Groovy’s @Canonical to Auto‑Generate Equality, HashCode, toString and Copy Methods
Use Groovy’s @Canonical annotation to automatically generate equals, hashCode, toString and a copy method for a simple value object, then verify with compilation and unit tests while watching for mutable‑field pitfalls.
09 Jun 2026, 02:31 UTC

When to use @Canonical
If you need a simple value object in Groovy, the @Canonical annotation automatically creates equals(), hashCode(), toString() and a copy() method based on the class’s declared properties.
How it works
Define a class with mutable fields and annotate it with @Canonical. Groovy generates the four methods at compile time, using the property names and their types.
@Canonical
class Person {
String first
String last
}
With this definition you can:
- Create instances as
new Person('John', 'Doe'). - Compare two persons with
==(value equality). - Use the object as a map key without worrying about
hashCodemismatches. - Call
def other = person.copy()to obtain a shallow copy.
Verification steps
- Compile the class with the Groovy compiler, e.g.
groovyc Person.groovy. This requires write permission in the source directory. - Decompile the generated
.classfile (usingjavap -cor a decompiler) and confirm that the methodsequals,hashCode,toStringandcopyexist. - Write a unit test:
def p1 = new Person('Alice', 'Smith') def p2 = new Person('Alice', 'Smith') assert p1 == p2 // true aSSERT p1.hashCode() == p2.hashCode() def p3 = p1.copy() assert p3 != p1 // distinct object - Modify a property after construction and re‑run the equality check; the hash code must change accordingly, proving the methods are bound to the mutable fields.
Key limitations
- Only works on Groovy classes; applying the annotation to a plain Java class does nothing and may mislead readers.
- The generated copy constructor performs a shallow copy, so nested mutable objects are shared and can cause side effects.
- It cannot handle lazy, computed, or inherited properties, and will not generate code for abstract or final classes unless they are concrete subclasses.
Common mistakes
- Forgetting to include all relevant fields, which leads to incorrect equality or hash code results.
- Using mutable fields that are changed after the object is used as a map key, breaking the contract of
hashCode. - Defining a custom
equalsorhashCodemethod; @Canonical will overwrite it, potentially breaking your intended logic.
When to avoid @Canonical
If your class contains immutable properties, derived values, or needs deep copying, consider writing the methods manually or using a dedicated data‑class library.
Summary
@Canonical provides a quick way to create value objects in Groovy, but you must be aware of its constraints and verify the generated code.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.