Groovy comparison operator overloading issues

I am building an analytic application using Groovy and require very forgiving math operators regardless of the data format. I achieve this by overloading the operator, improving (in my case) the default flexibility of the Groovy type in many cases. As an example, I need 123.45f + "05" equal to 128.45f. Groovy downgrades to String by default and I get 123.4505.

Most of the time, my overloading works pretty well, but not for comparison operators. I have followed several discussions about this, but I am not getting an answer and I am looking for ideas. I understand that the goal is to overload compareTo () (something like lessThan), but Groovy seems to ignore this and instead tries to do its own smart comparison - eg. DefaultTypeTransformation.compareTo (Object left, Object right) which fails with mixed types.

Unfortunately this is necessary for me because a wrong comparison of two values ​​would compromise the whole solution and I have no control over some of the data types being parsed (e.g. vendor data structures).

For example, I need the following:

Float f = 123.45f;
String s = "0300";

Assert.assertTrue( f < s );

      

I have many permutations of these, but my attempt to overload includes (let's just assume my JavaTypeUtil does what I need, if I can get Groovy to call it):

// overloads on startup, trying to catch all cases
Float.metaClass.compareTo = {
    Object o -> JavaTypeUtil.compareTo(delegate, o)  }

Float.metaClass.compareTo = {
    String s -> JavaTypeUtil.compareTo(delegate, s)  }

Object.metaClass.compareTo = {
    String s -> JavaTypeUtil.compareTo(delegate, s) }

Object.metaClass.compareTo = {
    Object o -> JavaTypeUtil.compareTo(delegate, o) }

      

When I try to execute the above test, none of them get called and instead I get:

    java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Float
at java.lang.Float.compareTo(Float.java:50)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareToWithEqualityCheck(DefaultTypeTransformation.java:585)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareTo(DefaultTypeTransformation.java:540)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareTo(ScriptBytecodeAdapter.java:690)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareLessThan(ScriptBytecodeAdapter.java:710)
at com.modelshop.datacore.generator.GroovyMathTests.testMath(GroovyMathTests.groovy:32)

      

Debugging through I see that the <operator goes straight to ScriptBytecodeAdapter.compareLessThan (), and the implementation of that seems to ignore the overloaded compareTo () here:

In DefaultTypeTransformations.java:584 (2.4.3)

        if (!equalityCheckOnly || left.getClass().isAssignableFrom(right.getClass())
                || (right.getClass() != Object.class && right.getClass().isAssignableFrom(left.getClass())) //GROOVY-4046
                || (left instanceof GString && right instanceof String)) {
            Comparable comparable = (Comparable) left;
            return comparable.compareTo(right);          // <--- ***
        }

      

In a desperate attempt, I also tried to overload compareLessThan, but now I grab it, I don't know there is any way to jump before <display in Groovy.

        Float.metaClass.compareLessThan << {
            Object right -> JavaTypeUtil.compareTo(delegate, right) < 0 }
        Float.metaClass.compareLessThan << {
            String right -> JavaTypeUtil.compareTo(delegate, right) < 0 }

      

Any thoughts on work? Thank!

+3


source to share


2 answers


Partially you need to include static

like this

Float.metaClass.static.compareTo = { String s -> 0 }

      

This does the job f.compareTo(s)

, but the operator <

is still not working. This is a known limitation . The only operators that can be overloaded are mentioned in the documentation. Perhaps you can make a custom AST to change all of these operators to compareTo()

.

But that's not the whole story. f <=> s

also doesn't work despite <=>

delegation compareTo()

. I believe this is because it Float

does not implement Comparable<Object>

or Comparable<String>

, only Comparable<Float>

. While I'm not sure exactly where in the chain Groovy decides not to use this method, you can see that this is not limited to Groovy's math classes. It doesn't work either.

Foo.metaClass.compareTo = { String s -> 99 }
new Foo() <=> ''

class Foo implements Comparable<Foo> {
    int compareTo(Foo o) {
        0
    }
}

      



I think Groovy is doing some pre-check to prevent the metaclass from working. Whatever check it does, it definitely checks the implemented interfaces, because it is for a different reason.

Foo.metaClass.compareTo = { String s -> 99 }
new Foo() <=> ''

class Foo {
    int compareTo(Foo o) {
        0
    }
}

      

In both of these examples, is replaced <=>

by compareTo()

.

This question has been asked a couple of times before , but I don't know. I've seen a good explanation why. You can ask the user for a mailing list . I'm sure Jochen or Cedric can explain why.

+2


source


I assume your closure is compareTo

pending Object

; so when you call compareTo

with String

, your closure is not called at all.

I can only think of the following, being precise when specifying the type of the closure input parameter:



Float.metaClass.compareTo = { Integer n -> aStaticHelperMethod(n) }
Float.metaClass.compareTo = { String s -> aStaticHelperMethod(s) }
Float.metaClass.compareTo = { SomeOtherType o -> aStaticHelperMethod(o) }

      

0


source







All Articles