This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
Best practises for implementing Equals and Hash methods | |
Source: http://www.artima.com/lejava/articles/equality.html | |
------------------------------------------------------------ | |
Common pitfalls when overriding equals: | |
- Defining equals with the wrong signature. | |
- Changing equals without also changing hashCode. | |
- Defining equals in terms of mutable fields. | |
- Failing to define equals as an equivalence relation. | |
The contract of the equals method in Object specifies that equals must | |
implement an equivalence relation on non-null objects: | |
It is reflexive: | |
for any non-null value x, the expression x.equals(x) should return true. | |
It is symmetric: | |
for any non-null values x and y, x.equals(y) should return true if and only if y.equals(x) returns true. | |
It is transitive: | |
for any non-null values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, | |
then x.equals(z) should return true. | |
It is consistent: | |
for any non-null values x and y, multiple invocations of x.equals(y) should consistently return true | |
or consistently return false, provided no information used in equals comparisons on | |
the objects is modified. For any non-null value x, x.equals(null) should return false. | |
**/ | |
shared abstract class Colour(shared String name) of yellow | orange | green { | |
shared actual Boolean equals(Object that) { | |
if (is Colour that) { | |
return that.canEqual(this) && name == that.name; | |
} | |
return false; | |
} | |
shared actual default Integer hash => name.hash; | |
shared default Boolean canEqual(Object that) => that is Colour; | |
} | |
shared object yellow extends Colour("yellow") {} | |
shared object orange extends Colour("orange") {} | |
shared object green extends Colour("green") {} | |
shared interface Coloured { | |
shared formal Colour colour; | |
} | |
shared class Fruit(name) { | |
shared String name; | |
shared actual default Boolean equals(Object that) { | |
if (is Fruit that) { | |
return that.canEqual(this) && name == that.name; | |
} | |
return false; | |
} | |
shared actual default Integer hash => name.hash; | |
shared default Boolean canEqual(Object that) => that is Fruit; | |
} | |
shared class ColouredFruit(String name, colour) extends Fruit(name) satisfies Coloured { | |
shared actual Colour colour; | |
shared actual Boolean equals(Object that) { | |
if (is ColouredFruit that) { | |
return that.canEqual(this) && name.equals(that.name) && colour.equals(that.colour); | |
} | |
return false; | |
} | |
shared actual default Integer hash => name.hash + colour.hash; | |
shared actual default Boolean canEqual(Object that) => that is ColouredFruit; | |
} | |
/** | |
comparefruits() prints | |
banana1 equal to banana2 = false | |
banana2 equal to banana1 = false | |
**/ | |
void comparefruits() { | |
Fruit banana1 = Fruit("banana"); | |
ColouredFruit banana2 = ColouredFruit("banana", yellow); | |
print("banana1 equal to banana2 = ``banana1.equals(banana2)``"); | |
print("banana2 equal to banana1 = ``banana2.equals(banana1)``"); | |
} |
you could defined :
ReplyDeleteshared class Fruit(name) {
shared default Boolean canEqual(Fruit that) => true;
}
shared class ColouredFruit(String name, colour) extends Fruit(name) satisfies Coloured {
shared actual default Boolean canEqual(Fruit that) => that is ColouredFruit;
}
(In ceylon, we have to declare a method 'shared' to be able to declare it 'default' and refine it ? we don't have the 'protected' java equivalent ?)