【问题标题】:What is *so* wrong with case class inheritance?案例类继承有什么 *so* 错误?
【发布时间】:2012-06-22 15:06:59
【问题描述】:

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

【问题讨论】:

    标签: scala inheritance case-class


    【解决方案1】:

    一个字:平等

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

    1. 所有xx equals xtrue(反身)
    2. 对于xyz;如果x equals yy equals z 然后x equals z(传递)
    3. 对于xy;如果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 did not work as expectedinteraction with the pattern matcher

    【讨论】:

    • 稍微详细说明一下怎么样 :) ?
    • 在 OO 范式中,这种不对称等价似乎很有用,就像在类型级别 ColoredPoint 是-a Point 但反之亦然。可能不得不称它为equals 以外的其他名称……也许subEquals
    • @LuigiPlinge 可能是 canReplacesupersedesspecifiesoverrides 的反向关系?任何表明它的>=-ness(或>:,如果你喜欢)。我用>= 来命名它似乎比<= 更容易。
    • 泛型 equals 很容易实现,它可以满足相等性,使类成为比较的成员。复制的东西看起来只是一个错误,与模式匹配器的交互应该可以工作,就像它对非案例类层次结构一样。
    • case class 只有在没有父级覆盖它的情况下才会得到equals,所以在这种情况下,ColoredPoint 将使用Pointequals/hashCode(我没有知道这在 2012 年是否已经是这种情况),它是对称的(并且是反射和传递的)。您可能会争辩 ColoredPoint(0, 0, RED) == ColoredPoint(0, 0, GREEN) 不直观,我同意,但问题不在于案例类继承:如果 Point 是覆盖 equals 的非 case 类,您将遇到完全相同的问题。 copy 更是个问题。
    【解决方案2】:

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

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

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

    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) = ???
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-16
      • 2018-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-26
      相关资源
      最近更新 更多