Reputation: 59844
I am building DSL and try to define a custom class CustomClass
that you can use in expressions like
def result = customInstance >= 100 ? 'a' : 'b'
if (customInstance == 'hello') {...}
Groovy doesn't call ==
when your class defines equals
and implements Comparable
(defines compareTo
) at the same time.
Instead Groovy calls compareToWithEqualityCheck
which has a branching logic. And unless your custom DSL class is assignable from String
or Number
your custom compareTo
won't be called for the example above.
You can't extend CustomClass
with String
.
I feel like I am missing something. Hope you can help me figure out how to implement a simple case like I showed above.
Upvotes: 4
Views: 320
Reputation: 1331
Here is a short answer first: You could extend GString
for the CustomClass
. Then its compareTo
method will be called in both cases - when you check for equality and when you actually compare.
Edit: Considering the following cases, it will work for 1 and 2, but not for 3.
customInstance >= 100 // case 1
customInstance == 'hallo' // case 2
customInstance == 10 // case 3
Now I will explain what I understand from the implementation in Groovy's ScriptBytecodeAdapter
and DefaultTypeTransformation
.
For the ==
operator, in case Comparable is implemented (and there is no simple identity), it tries to use the interface method compareTo, hence the same logic that is used for other comparison operators. Only if Comparable is not implemented it tries to determine equality based on some smart type adjustments and as an ultima ratio falls back to calling the equals method. This happens in DefaultTypeTransformation.compareEqual#L603-L608
For all other comparison operators such as >=
, Groovy delegates to the compareToWithEqualityCheck
method. Now this method is called with the equalityCheckOnly
flag set to false, while it is set to true for the first case when it the invocation originates from the ==
operator. Again there is some Groovy smartness happening based on the type of the left side if it is Number, Character, or String. If none applies it ends up calling the compareTo method in DefaultTypeTransformation.compareToWithEqualityCheck#L584-L586.
Now, this happens only if
!equalityCheckOnly || left.getClass().isAssignableFrom(right.getClass())
|| (right.getClass() != Object.class && right.getClass().isAssignableFrom(left.getClass())) //GROOVY-4046
|| (left instanceof GString && right instanceof String)
There are some restrictions for the case of equalityCheckOnly
, hence when we come from the ==
operator. While I can not explain all of those I believe these are to prevent exceptions to be thrown under specific circumstances, such as the issue mentioned in the comment.
For brevity I omitted above that there are also cases that are handled upfront in the ScriptBytecodeAdapter
and delegated to equals
right away, if left and right hand side are both of the same type and one of Integer, Double or Long.
Upvotes: 1