以这种方式覆盖 `equals()` 的缺点是什么?
What are the downsides of overriding `equals()` in this way?
考虑 Java 的不可变 ComplexNumber
class 的常见示例(它没有像 C# 那样的 ComplexNumber
结构)。当然有必要重写toString()
、equals()
和hashCode()
.
与 Scala 不同,我们不能使用智能转换(这是 IntelliJ Kotlin 术语)进行类型匹配。这种思路让我写下了这个 equals()
override:
public boolean equals(ComplexNumber other) {
if (other == null) return false; // Thanks, Andreas
if (Double.doubleToLongBits(this.real) != Double.doubleToLongBits(other.real)) return false;
return Double.doubleToLongBits(this.imag) == Double.doubleToLongBits(other.imag);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this.getClass() != obj.getClass()) return false;
return (this.equals((ComplexNumber) obj));
}
目前我并不太担心因机器精度损失而给予任何容限运行ce。
在 Mathematica 中,以错误的顺序定义事物会导致意想不到的问题。在 Java 中没有那么多。我调换了两个 equals()
的顺序,添加了打印行和 运行 测试。
equals(Object) invoked
equals(ComplexNumber) invoked
div
minus
times
plus
toString
equals(Object) invoked
equals(Object) invoked
equals(ComplexNumber) invoked
getRealPart
getImagPart
equals(Object) invoked
equals(ComplexNumber) invoked
equals(Object) invoked
equals(ComplexNumber) invoked
hashCode
equals(Object) invoked
equals(ComplexNumber) invoked
equals(Object) invoked
equals(ComplexNumber) invoked
equals(Object) invoked
equals(Object) invoked
equals(ComplexNumber) invoked
equals
无论顺序如何,它都通过了我测试中的所有相关测试 class,但我感觉如果我这样做,我不是在测试会成为问题的东西。不需要创建最终副本,而是需要在调用堆栈上再做一件事,所以从性能方面来说,如果这不是真正的消耗,那么这可能是一种浪费。我也觉得这可能是 Scala REPL 中的一个问题。
在纯粹实用的层面上,我非常满足于在这个上使用标准生成的 equals()
覆盖。但我很好奇这样做的缺点,除了它不符合习惯。
这是一个很好的实现。 Eric Evans 在他的书 "domain driven design" 中以这种方式实现了 ValueObject 的相等性。他将“等于”称为“sameValuAs”以区分它们和覆盖的 equals() 调用 sameValueAs
类似于您的含义。
当 class 类型不是使用 instanceof
方法的 ComplexNumber 类型时,我会建议将 equals 方法修改为 return false。这是一个例子-
public boolean sameValueAs(ComplexNumber other) {
if (other == null) return false;
if (Double.doubleToLongBits(this.real) != Double.doubleToLongBits(other.real)) return false;
return Double.doubleToLongBits(this.imag) == Double.doubleToLongBits(other.imag);
}
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if (!(obj instanceof ComplexNumber)) return false;
return sameValueAs((ComplexNumber) obj));
}
考虑 Java 的不可变 ComplexNumber
class 的常见示例(它没有像 C# 那样的 ComplexNumber
结构)。当然有必要重写toString()
、equals()
和hashCode()
.
与 Scala 不同,我们不能使用智能转换(这是 IntelliJ Kotlin 术语)进行类型匹配。这种思路让我写下了这个 equals()
override:
public boolean equals(ComplexNumber other) {
if (other == null) return false; // Thanks, Andreas
if (Double.doubleToLongBits(this.real) != Double.doubleToLongBits(other.real)) return false;
return Double.doubleToLongBits(this.imag) == Double.doubleToLongBits(other.imag);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this.getClass() != obj.getClass()) return false;
return (this.equals((ComplexNumber) obj));
}
目前我并不太担心因机器精度损失而给予任何容限运行ce。
在 Mathematica 中,以错误的顺序定义事物会导致意想不到的问题。在 Java 中没有那么多。我调换了两个 equals()
的顺序,添加了打印行和 运行 测试。
equals(Object) invoked
equals(ComplexNumber) invoked
div
minus
times
plus
toString
equals(Object) invoked
equals(Object) invoked
equals(ComplexNumber) invoked
getRealPart
getImagPart
equals(Object) invoked
equals(ComplexNumber) invoked
equals(Object) invoked
equals(ComplexNumber) invoked
hashCode
equals(Object) invoked
equals(ComplexNumber) invoked
equals(Object) invoked
equals(ComplexNumber) invoked
equals(Object) invoked
equals(Object) invoked
equals(ComplexNumber) invoked
equals
无论顺序如何,它都通过了我测试中的所有相关测试 class,但我感觉如果我这样做,我不是在测试会成为问题的东西。不需要创建最终副本,而是需要在调用堆栈上再做一件事,所以从性能方面来说,如果这不是真正的消耗,那么这可能是一种浪费。我也觉得这可能是 Scala REPL 中的一个问题。
在纯粹实用的层面上,我非常满足于在这个上使用标准生成的 equals()
覆盖。但我很好奇这样做的缺点,除了它不符合习惯。
这是一个很好的实现。 Eric Evans 在他的书 "domain driven design" 中以这种方式实现了 ValueObject 的相等性。他将“等于”称为“sameValuAs”以区分它们和覆盖的 equals() 调用 sameValueAs
类似于您的含义。
当 class 类型不是使用 instanceof
方法的 ComplexNumber 类型时,我会建议将 equals 方法修改为 return false。这是一个例子-
public boolean sameValueAs(ComplexNumber other) {
if (other == null) return false;
if (Double.doubleToLongBits(this.real) != Double.doubleToLongBits(other.real)) return false;
return Double.doubleToLongBits(this.imag) == Double.doubleToLongBits(other.imag);
}
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if (!(obj instanceof ComplexNumber)) return false;
return sameValueAs((ComplexNumber) obj));
}