Reputation: 27830
If I dynamically override equals
and hashCode
methods for a class, calling those methods directly calls overridden versions, but using them for set uses non overridden versions. Why is it so and is it still possible to dynamically override those two methods for all usages?
class SuperClass {
public boolean equals(Object other) {
println 'non overridden equals called'
false
}
public int hashCode() {
println 'non overridden hashCode called'
1
}
}
SuperClass.metaClass.equals = { Object other ->
println 'overridden equals called'
true
}
SuperClass.metaClass.hashCode = { ->
println 'overridden hashCode called'
1
}
def a = new SuperClass()
def b = new SuperClass()
println a.hashCode() // overriden hashCode called
println b.hashCode() // overriden hashCode called
println a.equals(b) // overriden equals called
println([a, b].toSet().size()) // non overriden methods called, returns 2 instead of 1
Upvotes: 4
Views: 2295
Reputation: 171144
Calling toSet()
on a List
invokes the following code:
Set<T> answer = new HashSet<T>(self.size());
answer.addAll(self);
return answer;
Now HashSet
(a Java class) has no concept of the metaClass
, and so does not see your overloaded hashCode
and equals
methods. Therefore you get 2 items in your set.
What you can do is call unique
on your list first of all:
println( [a, b].unique().toSet().size() )
As this goes through the invoker so knows about the metaClass
and should give you a set containing one element.
In practice, I'd avoid changing the hashCode
method via the metaClass. As you can see, it's hard to know exactly when it will be handled, and things where it is handled well may not be expecting the hashCode to change from moment to moment.
Upvotes: 3