为什么scala不能更容易地支持Equals?

时间:2015-10-10 15:31:45

标签: scala

阅读Martin的书,关于平等的章节Object equality chapter人们可能会注意到在scala中正确实现equals(实际上在任何其他语言中)并不是非常简单。然而scala是非常强大和敏捷的,我无法相信它不能简化一些事情。我知道scala为case类生成了正确的equals,所以我想知道为什么它不能为普通类生成简化?

为了表明我的观点,我写了一个例子,我怎么看这应该是这样的。它可能有缺陷,我必须使用ClassTag我知道因为性能而导致equals这样的基本问题是非常错误的(任何提示如果没有ClassTag我怎么能这样做?) ,但是认为scala可以为case类生成正确的equals,我会说它应该能够为普通类生成正确的代码,让开发人员提供应该用来比较对象的Key

trait Equality[T] extends Equals {
  val ttag: ClassTag[T]
  def Key: Seq[Any]

  def canEqual(other: Any): Boolean = other match {
    case that: Equality[_] if that.ttag == ttag => true
    case _ => false
  }

  override def equals(other: Any): Boolean =
   other match {
     case that: Equality[T] => canEqual(that) && Key == that.Key
     case _ => false
   }

  override def hashCode = Key.foldLeft(1)((x, y) => 41 * x + y.hashCode)
}

然后你可以像这样使用它:

class Point(val x: Int, val y: Int)(implicit val ttag: ClassTag[Point]) extends Equality[Point]{
    override def Key: Seq[Any] = Seq(x, y)
}

我对ClassTags的了解不多,所以我可能错了,但似乎有效。仍然不是我要问的 - 我想知道是否有任何严重的原因,为什么scala本身不简化实施平等检查?

2 个答案:

答案 0 :(得分:1)

没有这样的东西"正确等于"因为它取决于用例。
对于简单的情况,您建议可能有效,但在这种情况下,使用案例类也可以工作。问题在于,虽然对于简单的数据类它可以工作,但对于其他情况却同样如此。例如,当有继承时 - 正确的做法是什么?如果班级有私人成员 - 它应该是平等的一部分吗?公共吸气者应该是吗?当存在循环依赖时会发生什么? 其他案例类无法继承案例类的主要原因之一是the equality

答案 1 :(得分:1)

这是一个有趣的想法,我可以从评论中看到scalaz有这样的东西。我不确定我有完整的答案,但需要考虑的一些因素是:

案例类有点独特,因为你不应该继承它们。假设它们不属于分类,则甚至不需要canEqual。

这个想法很优雅,即使scala中的'+'也不是'语言功能'。将它作为语言特性而不是库(并且在Java中有许多实用程序来帮助实现hashcode / equals)并不一定更好。现有的“equals”方法不是语言特性,它只是父类Object的一个方法,继承自Java。

Scala仍然需要与Java一起玩,这可能是彻底改变equals工作方式的一个障碍。接口要求不能强加于scala可能希望继承的现有Java类。

当你考虑语法可能是什么样的语言功能时,我不确定实际可以消除或改变的是什么。
例如,开发人员仍然必须指定您建议的密钥。另外,为了能够支持canEqual不会改变的匿名子类和特征混合,使用语言功能,您仍然需要明确定义类标记。

如果你在语法上提供什么,如果它是一个语言特性而不是一个帮助库,那么分析可能会更有趣。我可能会遗漏这方面的一些方面。