【问题标题】:Problems overloading Groovy comparison operators重载 Groovy 比较运算符的问题
【发布时间】:2015-08-03 02:55:48
【问题描述】:

我正在使用 Groovy 构建一个分析应用程序,并且无论数据格式如何,我都需要非常宽容的数学运算符。我通过运算符重载来实现这一点,在许多情况下(就我而言)改进了默认的 Groovy 类型灵活性。例如,我需要 123.45f + "05" 等于 128.45f。默认情况下,Groovy 降级为 String,我得到 123.4505。

在大多数情况下,我的重载效果很好,但不适用于比较运算符。我已经对此进行了几次讨论,但我没有得到答案,我正在寻找想法。我认识到目标是重载 compareTo()(与类似 lessThan 的东西相比),但 Groovy 似乎忽略了这一点,而是尝试自己的智能比较 - 例如DefaultTypeTransformation.compareTo(Object left, Object right),在混合类型上失败。

不幸的是,这对我来说是必须的,因为不正确地比较两个值会损害整个解决方案,而且我无法控制正在分析的某些数据类型(例如供应商数据结构)。

例如,我需要以下工作:

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

Assert.assertTrue( f < s );

我有很多这些排列,但我尝试重载包括(假设我的 JavaTypeUtil 可以满足我的需要,如果我可以让 Groovy 调用它):

// 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) }

当我尝试上述测试时,这些都没有被调用,而是我得到:

    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)

通过调试,我看到

在 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);          // <--- ***
        }

在绝望的尝试中,我也尝试过重载compareLessThan,但我现在抓紧了,我不知道有什么办法可以跳到Groovy中的

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

对解决方法有什么想法吗?谢谢!

【问题讨论】:

    标签: groovy comparison operator-overloading


    【解决方案1】:

    部分原因是你需要包含static,像这样

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

    这使得f.compareTo(s) 工作,但&lt; 运算符仍然无法工作。这是known limitationdocumentation 中提到了唯一可以重载的运算符。可能您可以执行自定义 AST 将所有这些运算符更改为 compareTo()

    但这并不是故事的全部。 f &lt;=&gt; s 也不起作用,尽管 &lt;=&gt; 委托给 compareTo()。我相信这是因为Float 没有实现Comparable&lt;Object&gt;Comparable&lt;String&gt;,只有Comparable&lt;Float&gt;。尽管我不确定 Groovy 决定不使用该方法的确切位置,但您可以看到它不仅限于 Groovy 的数学类。这也行不通

    Foo.metaClass.compareTo = { String s -> 99 }
    new Foo() <=> ''
    
    class Foo implements Comparable<Foo> {
        int compareTo(Foo o) {
            0
        }
    }
    

    我认为 Groovy 正在做一些预解析验证,这会阻止元类的工作。无论它在做什么验证肯定会检查实现的接口,因为这会因不同的原因而失败

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

    在这两个示例中,将 &lt;=&gt; 替换为 compareTo() 有效。

    这个问题已经asked 几次before,但我还没有看到一个很好的解释。您可以尝试询问用户mailing list。我相信 Jochen 或 Cedric 能够解释原因。

    【讨论】:

    • 谢谢。嗯,不确定我是否得到静电。 f.compareTo(s) 实际上像我实现的那样工作,它只是 <... groovy comparitor.compareto>
    • “作品”是指Float.metaClass.static.compareTo = { String s -&gt; 0 }; Float f = 7.0f; String s = 'str'; assert f.compareTo(s) == 0,它更接近,但仍然不是你想要的。
    • Jochen Keegan 提到的我可以给你更多细节。 compareTo 的调用或多或少是用 Java 方法调用逻辑完成的,所以看不到元类版本。此外,DefaultTypeTransformation.compareToWithEqualityCheck 将检查操作数的类型是否兼容 - 对于 A.compareTo(B),A 是 B 的子类。我们必须添加它,因为 compareTo 可能会抛出异常。与equals不同,compareTo不需要接受不适合的类型。而且人们不喜欢
    【解决方案2】:

    我想重点是您的compareTo 闭包期待Object;因此,当您使用 String 调用 compareTo 时,您的闭包根本不会被调用。

    我只能想到以下几点;指定闭包输入参数类型时要精确:

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

    【讨论】:

    • 谢谢 - 我已经完成了所有排列 Float -> Object, Float -> String, Object -> Object, Object -> String。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-07
    • 1970-01-01
    • 1970-01-01
    • 2011-03-07
    • 1970-01-01
    • 2018-11-21
    相关资源
    最近更新 更多