Groovy   发布时间:2022-04-12  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了在Groovy中,为什么’==’的行为会改变扩展Comparable的接口?大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试在Groovy中开发一个项目,我发现我的一些测试以一种奇怪的方式失败:@L_197_1@一个接口Version extends Comparable< Version>有两个具体的子类.两者都重写equals(Object)和compareTo(Version) – 但是,如果我尝试使用==比较具有不同具体类型的Version的两个实例,则即使显式equals和compareTo检查通过,相等检查也会失败.

如果我删除extends Comparable< Version>版本的一部分,我得到预期的行为 – ==给出与equals相同的结果.

我在其他地方读过Groovy委托==到equals(),除非该类实现了Comparable,在这种情况下它委托compareTo.但是,我发现两个都声明版本的两个实例相等而且==检查失败的情况.

我创建了一个演示此行为的SSCCE @L_404_0@.

完整代码也在下面提供:

// Interface extending Comparable
interface Super extends Comparable<Super> {
    int getValue()
}

class SubA implements Super {
    int getValue() { 1 }
    int compareTo(Super that) { this.value <=> that.value }
    Boolean equals(Object o) {
        if (o == null) return false
        if (!(o instanceof Super)) return false
        this.value == o.value
    }
}

class SubB implements Super {
    int getValue() { 1 }
    int compareTo(Super that) { this.value <=> that.value }
    Boolean equals(Object o) {
        if (o == null) return false
        if (!(o instanceof Super)) return false
        this.value == o.value
    }
}

// Interface not extending Comparable
interface AnotherSuper {
    int getValue()
}

class AnotherSubA implements AnotherSuper {
    int getValue() { 1 }
    Boolean equals(Object o) {
        if (o == null) return false
        if (!(o instanceof AnotherSuper)) return false
        this.value == o.value
    }
}

class AnotherSubB implements AnotherSuper {
    int getValue() { 1 }
    Boolean equals(Object o) {
        if (o == null) return false
        if (!(o instanceof AnotherSuper)) return false
        this.value == o.value
    }
}


// check with comparable versions
def a = new SubA()
def b = new SubB()

println "Comparable versions equality check: ${a == b}"
println "Explicit comparable equals check: ${a.equals(b)}"
println "Explicit comparable compareTo check: ${a.compareTo(b)}"

// check with non-comparable versions
def anotherA = new AnotherSubA()
def anotherB = new AnotherSubB()

println "Non-comparable versions equality check: ${anotherA == anotherB}"
println "Explicit non-comparable equals check: ${anotherA.equals(anotherB)}"

我得到的回报是:

Comparable versions equality check: false
Explicit comparable equals check: true
Explicit comparable compareTo check: 0
Non-comparable versions equality check: true
Explicit non-comparable equals check: true

编辑
我想我明白为什么现在会发生这种情况,这要归功于Poundex与下面链接JIRA discussion.

从Groovy的DefaultTypeTransformation class开始,它用于处理相等/比较检查,我假设在评估x == y形式的语句时首先调用compareEqual方法

public static Boolean compareEqual(Object left,Object right) {
    if (left == right) return true;
    if (left == null || right == null) return false;
    if (left instanceof ComparablE) {
        return compareToWithEqualitycheck(left,right,truE) == 0;
    }
    // handle arrays on both sides as special case for efficiency
    Class leftClass = left.getClass();
    Class rightClass = right.getClass();
    if (leftClass.isArray() && rightClass.isArray()) {
        return compareArrayEqual(left,right);
    }
    if (leftClass.isArray() && leftClass.getComponentType().isPrimitive()) {
        left = primitiveArrayToList(left);
    }
    if (rightClass.isArray() && rightClass.getComponentType().isPrimitive()) {
        right = primitiveArrayToList(right);
    }
    if (left instanceof Object[] && right instanceof List) {
        return DefaultGroovymethods.equals((Object[]) left,(List) right);
    }
    if (left instanceof List && right instanceof Object[]) {
        return DefaultGroovymethods.equals((List) left,(Object[]) right);
    }
    if (left instanceof List && right instanceof List) {
        return DefaultGroovymethods.equals((List) left,(List) right);
    }
    if (left instanceof Map.Entry && right instanceof Map.Entry) {
        Object k1 = ((Map.Entry)left).getKey();
        Object k2 = ((Map.Entry)right).getKey();
        if (k1 == k2 || (k1 != null && k1.equals(k2))) {
            Object v1 = ((Map.Entry)left).getValue();
            Object v2 = ((Map.Entry)right).getValue();
            if (v1 == v2 || (v1 != null && DefaultTypeTransformation.compareEqual(v1,v2)))
                return true;
        }
        return false;
    }
    return ((Boolean) InvokerHelper.invokeMethod(left,"equals",right)).BooleanValue();
}

请注意,如果表达式的LHS是Comparable的实例,就像我提供的示例中那样,则将比较委托给compareToWithEqualitycheck:

private static int compareToWithEqualitycheck(Object left,Object right,Boolean equalitycheckOnly) {
    if (left == right) {
        return 0;
    }
    if (left == null) {
        return -1;
    }
    else if (right == null) {
        return 1;
    }
    if (left instanceof ComparablE) {
        if (left instanceof number) {
            if (right instanceof Character || right instanceof number) {
                return DefaultGroovymethods.compareTo((number) left,castTonumber(right));
            }
            if (isValidCharacterString(right)) {
                return DefaultGroovymethods.compareTo((number) left,ShortTypeHandling.casttochar(right));
            }
        }
        else if (left instanceof Character) {
            if (isValidCharacterString(right)) {
                return DefaultGroovymethods.compareTo((Character)left,ShortTypeHandling.casttochar(right));
            }
            if (right instanceof number) {
                return DefaultGroovymethods.compareTo((Character)left,(number)right);
            }
        }
        else if (right instanceof number) {
            if (isValidCharacterString(left)) {
                return DefaultGroovymethods.compareTo(ShortTypeHandling.casttochar(left),(number) right);
            }
        }
        else if (left instanceof String && right instanceof Character) {
            return ((String) left).compareTo(right.toString());
        }
        else if (left instanceof String && right instanceof GString) {
            return ((String) left).compareTo(right.toString());
        }
        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);
        }
    }

    if (equalitycheckOnly) {
        return -1; // anything other than 0
    }
    throw new GroovyRuntimeException(
            messageformat.format("CAnnot compare {0} with value ''{1}'' and {2} with value ''{3}''",left.getClass().getName(),left,right.getClass().getName(),right));
}

底部附近,该方法一个块,它将比较委托给compareTo方法,但仅限于满足某些条件.在我提供的示例中,没有满足这些条件,包括isAssignableFrom检查,因为我提供的示例类(以及我的项目中给出问题的代码)是兄弟,因此不能彼此分配.

我想我明白为什么检查现在失败了,但我仍然对以下事情感到困惑:

>我该如何解决这个问题?
>这背后的理由是什么?这是一个错误还是一个设计功能?是否有任何理由说明为什么普通超类的两个子类不能相互比较?

解决方法

为什么Comparable用于==如果存在很容易的答案.这是因为Bigdecimal.如果你将Bigdecimal设为“1.0”和“1.00”(使用字符串不是双倍!),你会得到两个根据等于不相等的Bigdecimal,因为它们没有相同的比例.它们在价值方面是平等的,这就是为什么compareTo会将它们看作是平等的.

当然还GROOVY-4046,它显示了直接调用compareTo将导致ClassCastException的情况.由于此异常是意外的,因此我们决定添加可分配性检查.

解决此问题,您可以使用< =>相反,你已经找到了.是的,它们仍然通过DefaultTypeTransformation,因此您可以比较例如int和long.如果你不想要那个,那么直接调用compareTo就可以了.如果我误解了你并且你想要实际上有平等,那么你当然应该称之为等于.

大佬总结

以上是大佬教程为你收集整理的在Groovy中,为什么’==’的行为会改变扩展Comparable的接口?全部内容,希望文章能够帮你解决在Groovy中,为什么’==’的行为会改变扩展Comparable的接口?所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。