scala.Equals特性中的canEqual() [英] canEqual() in the scala.Equals trait

查看:542
本文介绍了scala.Equals特性中的canEqual()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

来自源代码scala/Equals.scala(此处 ):

package scala
trait Equals extends scala.Any {
  def canEqual(that: scala.Any): scala.Boolean
  def equals(that: scala.Any): scala.Boolean
}

在文档中说:

应该从每个设计良好的equals方法中调用的方法,该方法打开后会在子类中被覆盖.

A method that should be called from every well-designed equals method that is open to be overridden in a subclass.

我随机选择了一个扩展scala.Equals的类,它很容易理解.我选择了scala.Tuple2[+T1, +T2],它扩展了特征scala.Product[T1, T2],又扩展了特征scala.Product,又扩展了特征scala.Equals.

I randomly picked a class which extends scala.Equals and which is simple enough to understand. I picked scala.Tuple2[+T1, +T2], which extends the trait scala.Product[T1, T2], which in turn extends the trait scala.Product, which in turn extends the trait scala.Equals.

不幸的是,似乎因为scala.Tuple2 case类,所以canEqual()equals()方法是自动生成的,因此无法在源代码scala/Tuple2.scala中找到( 此处).

Unfortunately, it seems that because scala.Tuple2 is a case class, the canEqual() and equals() methods are automatically generated and therefore could not be found in the source code scala/Tuple2.scala (here).

我的问题是:

  • 何时扩展性状scala.Equals的好时机?
  • canEqual()应该如何实施?
  • equals()中使用canEqual()的最佳实践(或样板)是什么?
  • When is it a good time to extend the trait scala.Equals?
  • How should canEqual() be implemented?
  • What are the best practices (or boilerplate) to use canEqual() in equals()?

提前谢谢!

PS:如果有关系的话,我正在使用Scala 2.11.7.

PS: In case if it matters, I'm using Scala 2.11.7.

推荐答案

canEquals方法用于满足以下要求:equals应该是对称的-即,(且仅)当a.equals(b)为true时,则b.equals(a)也应为true.将类的实例与子类的实例进行比较时,可能会出现问题.例如

The canEquals method is used to cover the expectation that equals should be symmetric - that is, if (and only if) a.equals(b) is true, then b.equals(a) should also be true. Problems with this can arise when comparing an instance of a class with an instance of a sub-class. Eg.

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确保两个实体具有相同(子)类型:

To fix this, ensure the two entities are of the same (sub-)type using canEqual in the definition of equals:

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特性中的canEqual()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆