scala.Equals 特征中的 canEqual()
canEqual() in the scala.Equals trait
来自源代码scala/Equals.scala
(here):
package scala
trait Equals extends scala.Any {
def canEqual(that: scala.Any): scala.Boolean
def equals(that: scala.Any): scala.Boolean
}
在文档中,它说:
A method that should be called from every well-designed equals method that is open to be overridden in a subclass.
我随机选了一个 class 扩展 scala.Equals
并且很简单易懂。我选择了 scala.Tuple2[+T1, +T2]
,它扩展了特征 scala.Product[T1, T2]
,它又扩展了特征 scala.Product
,它又扩展了特征 scala.Equals
.
不幸的是,似乎因为 scala.Tuple2
是 案例 class,所以 canEqual()
和 equals()
方法是自动的生成因此无法在源代码中找到 scala/Tuple2.scala
(here).
我的问题是:
- 什么时候是扩展特征的好时机
scala.Equals
?
canEqual()
应该如何实施?
- 在
equals()
中使用 canEqual()
的最佳实践(或样板)是什么?
提前致谢!
PS:以防万一,我使用的是 Scala 2.11.7。
canEquals
方法用于覆盖 equals
应该是对称的期望 - 也就是说,如果(且仅当)a.equals(b)
为真,则 b.equals(a)
也应该是真的。将 class 的实例与子 class 的实例进行比较时,可能会出现问题。例如
class Animal(numLegs: Int, isCarnivore: Boolean) {
def equals(other: Any) = other match {
case that: Animal =>
this.numLegs == that.numLegs &&
this.isCarnivore == that.isCarnivore
case _ => false
}
}
class Dog(numLegs: Int, isCarnivore: Boolean, breed: String) extends Animal(numLegs, isCarnivore) {
def equals(other: Any) = other match {
case that: Dog =>
this.numLegs == that.numLegs &&
this.isCarnivore == that.isCarnivore &&
this.breed == that.breed
case _ => false
}
}
val cecil = new Animal(4, true)
val bruce = new Dog(4, true, "Boxer")
cecil.equals(bruce) // true
bruce.equals(cecil) // false - cecil isn't a Dog!
要解决此问题,请在 equals
的定义中使用 canEqual
确保两个实体属于相同(子)类型:
class Animal(numLegs: Int, isCarnivore: Boolean) {
def canEqual(other: Any) = other.isInstanceOf[Animal]
def equals(other: Any) = other match {
case that: Animal =>
that.canEqual(this) &&
this.numLegs == that.numLegs &&
this.isCarnivore == that.isCarnivore
case _ => false
}
}
class Dog(numLegs: Int, isCarnivore: Boolean, breed: String) extends Animal(numLegs, isCarnivore) {
def canEqual(other: Any) = other.isInstanceOf[Dog]
def equals(other: Any) = other match {
case that: Dog =>
that.canEqual(this) &&
this.numLegs == that.numLegs &&
this.isCarnivore == that.isCarnivore &&
this.breed == that.breed
case _ => false
}
}
val cecil = new Animal(4, true)
val bruce = new Dog(4, true, "Boxer")
cecil.equals(bruce) // false - call to bruce.canEqual(cecil) returns false
bruce.equals(cecil) // false
来自源代码scala/Equals.scala
(here):
package scala
trait Equals extends scala.Any {
def canEqual(that: scala.Any): scala.Boolean
def equals(that: scala.Any): scala.Boolean
}
在文档中,它说:
A method that should be called from every well-designed equals method that is open to be overridden in a subclass.
我随机选了一个 class 扩展 scala.Equals
并且很简单易懂。我选择了 scala.Tuple2[+T1, +T2]
,它扩展了特征 scala.Product[T1, T2]
,它又扩展了特征 scala.Product
,它又扩展了特征 scala.Equals
.
不幸的是,似乎因为 scala.Tuple2
是 案例 class,所以 canEqual()
和 equals()
方法是自动的生成因此无法在源代码中找到 scala/Tuple2.scala
(here).
我的问题是:
- 什么时候是扩展特征的好时机
scala.Equals
? canEqual()
应该如何实施?- 在
equals()
中使用canEqual()
的最佳实践(或样板)是什么?
提前致谢!
PS:以防万一,我使用的是 Scala 2.11.7。
canEquals
方法用于覆盖 equals
应该是对称的期望 - 也就是说,如果(且仅当)a.equals(b)
为真,则 b.equals(a)
也应该是真的。将 class 的实例与子 class 的实例进行比较时,可能会出现问题。例如
class Animal(numLegs: Int, isCarnivore: Boolean) {
def equals(other: Any) = other match {
case that: Animal =>
this.numLegs == that.numLegs &&
this.isCarnivore == that.isCarnivore
case _ => false
}
}
class Dog(numLegs: Int, isCarnivore: Boolean, breed: String) extends Animal(numLegs, isCarnivore) {
def equals(other: Any) = other match {
case that: Dog =>
this.numLegs == that.numLegs &&
this.isCarnivore == that.isCarnivore &&
this.breed == that.breed
case _ => false
}
}
val cecil = new Animal(4, true)
val bruce = new Dog(4, true, "Boxer")
cecil.equals(bruce) // true
bruce.equals(cecil) // false - cecil isn't a Dog!
要解决此问题,请在 equals
的定义中使用 canEqual
确保两个实体属于相同(子)类型:
class Animal(numLegs: Int, isCarnivore: Boolean) {
def canEqual(other: Any) = other.isInstanceOf[Animal]
def equals(other: Any) = other match {
case that: Animal =>
that.canEqual(this) &&
this.numLegs == that.numLegs &&
this.isCarnivore == that.isCarnivore
case _ => false
}
}
class Dog(numLegs: Int, isCarnivore: Boolean, breed: String) extends Animal(numLegs, isCarnivore) {
def canEqual(other: Any) = other.isInstanceOf[Dog]
def equals(other: Any) = other match {
case that: Dog =>
that.canEqual(this) &&
this.numLegs == that.numLegs &&
this.isCarnivore == that.isCarnivore &&
this.breed == that.breed
case _ => false
}
}
val cecil = new Animal(4, true)
val bruce = new Dog(4, true, "Boxer")
cecil.equals(bruce) // false - call to bruce.canEqual(cecil) returns false
bruce.equals(cecil) // false