Groovy ==的行为不符合规范

时间:2018-06-22 13:10:10

标签: groovy comparison operator-overloading

因此,根据the official documentation==在可比较对象上调用时的行为类似于compareTo

现在我有一个这样的课程:

class LargeNum implements Serializable, Comparable<BigInteger>

定义方法

@Override
int compareTo(BigInteger o) throws NullPointerException, ClassCastException {
    println "compared"
    return compareStuff()
}

您会期望这段代码:

LargeNum t = new LargeNum(1)
println t == new BigInteger(1)

打印出“比较”并调用compareStuff,尽管不是。我收到的唯一输出是:false

我对这是为什么真的感到困惑。覆盖equals也不能完全解决问题。

2 个答案:

答案 0 :(得分:2)

在这种情况下,文档不清晰-如果在实现{{的相同类型.compareTo(Object obj)的两个对象之间使用.equals(Object obj)运算符,则会调用==而不是T 1}}。在您的示例类中,Comparable<T>实现了LargeNum-在这种情况下

Comparable<BigInteger>

将调用new LargeNum(1) == new LargeNum(1) 方法,而不是.equals(Object obj)

如果您实现.compareTo(Object obj)并且进行比较

Comparable<LargeNum>

new LargeNum(1) == new LargeNum(1) 启动。请考虑以下示例:

.compareTo(Object obj)

运行此示例将产生以下输出:

class LargeNum implements Comparable<LargeNum> {

  private final BigInteger number

  LargeNum(BigInteger number) {
    this.number = number
  }

  @Override
  int compareTo(LargeNum num) {
    println "compared"
    return number <=> num.number
  }
}

LargeNum num1 = new LargeNum(1)
LargeNum num2 = new LargeNum(1)

println num1 == num2

在这种情况下,我可以使用compared true 方法覆盖==运算符吗?

简短回答-不。为了理解为什么和发生什么,我们需要深入研究一下字节码。下面的代码部分:

.equals(Object obj)

表示为反编译为Java的以下字节码:

LargeNum num1 = new LargeNum(1)
LargeNum num2 = new LargeNum(1)
BigInteger bigInt1 = new BigInteger(1)

println num1 == num2

println num1 == bigInt1

它表明LargeNum num1 = (LargeNum)ScriptBytecodeAdapter.castToType(var1[1].callConstructor(LargeNum.class, 1), LargeNum.class); LargeNum num2 = (LargeNum)ScriptBytecodeAdapter.castToType(var1[2].callConstructor(LargeNum.class, 1), LargeNum.class); BigInteger bigInt1 = (BigInteger)ScriptBytecodeAdapter.castToType(var1[3].callConstructor(BigInteger.class, 1), BigInteger.class); var1[4].callCurrent(this, ScriptBytecodeAdapter.compareEqual(num1, num2)); return var1[5].callCurrent(this, ScriptBytecodeAdapter.compareEqual(num1, bigInt1)); 实际上是num1 == bitInt1。如果我们看一下source code,我们会发现此方法在我们的情况下执行方法:

ScriptBytecodeAdapter.compareEqual(num1, bigInt1)

现在,如果我们看一下the implementation of this method的外观,我们会发现以下内容:

DefaultTypeTransformation.compareEqual(left, right);

它表明,对于执行以下部分的任何public static boolean compareEqual(Object left, Object right) { if (left == right) return true; if (left == null) return right instanceof NullObject; if (right == null) return left instanceof NullObject; 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(); // some other stuff here } 对象:

Comparable

The implementation of this method揭示了如果两个对象是相同类型或者可以将它们强制转换为通用类型,则会触发if (left instanceof Comparable) { return compareToWithEqualityCheck(left, right, true) == 0; } 方法。否则,它仅返回.compareTo(Object obj)。如果我们尝试比较-1LargeNum,就会发生这种情况-这两类之间没有共同的分母,因此至少对于Groovy而言,它们是不可比的。

我不知道这是否是错误,是否没有BigInteger方法的后备。另外,您始终可以直接调用特定的方法,例如:

.equals(Object obj)

num1.equals(bigInt1)

不过,我了解到num1.compareTo(bigInt1) // num1 <=> bigInt1 的{​​{1}}运算符的这种行为可能非常直观且令人讨厌。尤其是如果您定义要实现==的类-我希望任何对象都可以传递给覆盖的Comparable,但不是:/

希望有帮助。

答案 1 :(得分:1)

常规操作符==调用方法.equals(...)

此代码可以正常工作

class A {
    private final Integer i
    A(Integer i){
        this.i=i
    }
    public boolean equals(Object o){
        println "compare $this to $o"
        return i.equals(o)
    }
    String toString(){
        return "A[i=$i]"
    }
}

def a=new A(123)
println "${a==123}"

结果:

compare A[i=123] to 123
true