69

在寻找其他东西时,完全出于巧合,我偶然发现了一些关于恶魔案例类继承如何的评论。有一个东西叫做ProductN,坏蛋和国王,精灵和巫师,以及一些非常理想的属性是如何随着案例类继承而丢失的。那么案例类继承有什么问题呢?

4

2 回答 2

115

一个字:平等

case类提供了equals和的实现hashCode。等价关系,称为这样的equals工作(即必须具有以下属性):

  1. 对于所有人xx equals xtrue(反身的)
  2. 对于x, y, z; 如果x equals y然后(传递y equals zx equals z
  3. 对于x, y; 如果x equals y那么y equals x(对称)

只要您允许继承层次结构中的相等性,您就可以打破 2 和 3。以下示例简单地证明了这一点:

case class Point(x: Int, y: Int)
case class ColoredPoint(x: Int, y: Int, c: Color) extends Point(x, y) 

然后我们有:

Point(0, 0) equals ColoredPoint(0, 0, RED)

不是

ColoredPoint(0, 0, RED) equals Point(0, 0)

你可能会争辩说所有的类层次结构都可能有这个问题,这是真的。但是从开发人员的角度来看,案例类的存在专门用于简化相等性(以及其他原因),因此让它们以非直觉的方式表现将是对自己目标的定义!


还有其他原因;尤其是copy没有按预期工作以及与模式匹配器交互的事实。

于 2012-06-22T15:12:44.207 回答
-2

这并不完全正确。这比谎言更糟糕。

正如aepurniet所提到的,在任何情况下,限制定义区域的类后继者都必须重新定义相等性,因为模式匹配必须与相等性完全相同(如果尝试匹配PointColoredPoint那么它将不匹配,因为color它不存在)。

这有助于理解如何实现案例类层次结构的相等性。

case class Point(x: Int, y: Int)
case class ColoredPoint(x: Int, y: Int, c: Color) extends Point(x, y)

Point(0, 0) equals ColoredPoint(0, 0, RED)  // false
Point(0, 0) equals ColoredPoint(0, 0, null) // true

ColoredPoint(0, 0, RED) equals Point(0, 0)  // false
ColoredPoint(0, 0, null) equals Point(0, 0) // true

最终有可能满足相等关系的要求,即使是案例类的后继者(不覆盖相等性)。

case class ColoredPoint(x: Int, y: Int, c: String)
class RedPoint(x: Int, y: Int) extends ColoredPoint(x, y, "red")
class GreenPoint(x: Int, y: Int) extends ColoredPoint(x, y, "green")

val colored = ColoredPoint(0, 0, "red")
val red1 = new RedPoint(0, 0)
val red2 = new RedPoint(0, 0)
val green = new GreenPoint(0, 0)

red1 equals colored // true
red2 equals colored // true
red1 equals red2 // true

colored equals green // false
red1 equals green // false
red2 equals green // false

def foo(p: GreenPoint) = ???
于 2016-04-06T16:30:00.793 回答