Package groovy.transform
Annotation Type EqualsAndHashCode
-
@Documented @Retention(RUNTIME) @Target(TYPE) public @interface EqualsAndHashCodeClass annotation used to assist in creating appropriateequals()andhashCode()methods. It allows you to write classes in this shortened form:import groovy.transform.EqualsAndHashCode
The@EqualsAndHashCodeclass Person { String first, last int age } def p1 = new Person(first:'John', last:'Smith', age:21) def p2 = new Person(first:'John', last:'Smith', age:21) assert p1 == p2 def map = [:] map[p1] = 45 assert map[p2] == 45@EqualsAndHashCodeannotation instructs the compiler to execute an AST transformation which adds the necessary equals and hashCode methods to the class. ThehashCode()method is calculated using Groovy'sHashCodeHelperclass which implements an algorithm similar to the one outlined in the book Effective Java. Theequals()method compares the values of the individual properties (and optionally fields) of the class. It can also optionally call equals on the super class. Two different equals method implementations are supported both of which support the equals contract outlined in the javadoc forjava.lang.ObjectTo illustrate the 'canEqual' implementation style (see http://www.artima.com/lejava/articles/equality.html for further details), consider this class:
The generated@EqualsAndHashCodeclass IntPair { int x, y }equalsandcanEqualmethods will be something like below:public boolean equals(java.lang.Object other) if (other == null) return false if (this.is(other)) return true if (!(other instanceof IntPair)) return false if (!other.canEqual(this)) return false if (x != other.x) return false if (y != other.y) return false return true } public boolean canEqual(java.lang.Object other) { return other instanceof IntPair }If no further options are specified, this is the default style for@Canonicaland@EqualsAndHashCodeannotated classes. The advantage of this style is that it allows inheritance to be used in limited cases where its purpose is for overriding implementation details rather than creating a derived type with different behavior. This is useful when using JPA Proxies for example or as shown in the following examples:
Note that if you create any domain classes which don't have exactly the same contract as@Canonicalclass IntPair { int x, y } def p1 = new IntPair(1, 2) // overriden getter but deemed an IntPair as far as domain is concerned def p2 = new IntPair(1, 1) { int getY() { 2 } } // additional helper method added through inheritance but // deemed an IntPair as far as our domain is concerned@InheritConstructorsclass IntPairWithSum extends IntPair { def sum() { x + y } } def p3 = new IntPairWithSum(1, 2) assert p1 == p2 && p2 == p1 assert p1 == p3 && p3 == p1 assert p3 == p2 && p2 == p3IntPairthen you should provide an appropriateequalsandcanEqualmethod. The easiest way to achieve this would be to use the@Canonicalor@EqualsAndHashCodeannotations as shown below:
The alternative supported style regards any kind of inheritance as creation of a new type and is illustrated in the following example:@EqualsAndHashCode@TupleConstructor(includeSuperProperties=true)class IntTriple extends IntPair { int z } def t1 = new IntTriple(1, 2, 3) assert p1 != t1 && p2 != t1 && t1 != p3
The generated equals method will be something like below:@EqualsAndHashCode(useCanEqual=false)class IntPair { int x, y }public boolean equals(java.lang.Object other) if (other == null) return false if (this.is(other)) return true if (IntPair != other.getClass()) return false if (x != other.x) return false if (y != other.y) return false return true }This style is appropriate for final classes (where inheritance is not allowed) which have onlyjava.lang.Objectas a super class. Most@Immutableclasses fall in to this category. For such classes, there is no need to introduce thecanEqual()method. Note that if you explicitly setuseCanEqual=falsefor child nodes in a class hierarchy but have ittruefor parent nodes and you also havecallSuper=truein the child, then your generated equals methods will not strictly follow the equals contract. Note that when used in the recommended fashion, the two implementations supported adhere to the equals contract. You can provide your own equivalence relationships if you need, e.g. for comparing instances of theIntPairandIntTripleclasses discussed earlier, you could provide the following method inIntPair:boolean hasEqualXY(other) { other.x == getX() && other.y == getY() }Then for the objects defined earlier, the following would be true:assert p1.hasEqualXY(t1) && t1.hasEqualXY(p1) assert p2.hasEqualXY(t1) && t1.hasEqualXY(p2) assert p3.hasEqualXY(t1) && t1.hasEqualXY(p3)
There is also support for including or excluding fields/properties by name when constructing the equals and hashCode methods as shown here:import groovy.transform.*
@EqualsAndHashCode(excludes="z")@TupleConstructorpublic class Point2D { int x, y, z } assert new Point2D(1, 1, 1).equals(new Point2D(1, 1, 2)) assert !new Point2D(1, 1, 1).equals(new Point2D(2, 1, 1))@EqualsAndHashCode(excludes="y,z")@TupleConstructorpublic class Point2D { int x, y, z } assert new Point1D(1, 1, 1).equals(new Point1D(1, 1, 2)) assert new Point1D(1, 1, 1).equals(new Point1D(1, 2, 1)) assert !new Point1D(1, 1, 1).equals(new Point1D(2, 1, 1))- Since:
- 1.8.0
- Author:
- Paul King
- See Also:
HashCodeHelper
-
-
Optional Element Summary
Optional Elements Modifier and Type Optional Element Description booleancallSuperWhether to include super in equals and hashCode calculationsjava.lang.StringexcludesComma separated list of field and/or property names to exclude from the equals and hashCode calculations.booleanincludeFieldsInclude fields as well as properties in equals and hashCode calculationsjava.lang.StringincludesComma separated list of field and/or property names to include within the equals and hashCode calculations.booleanuseCanEqualGenerate a canEqual method to be used by equals
-