4

Squeryl定义了一个KeyedEntity覆盖的特征equals,检查 if 中的几个条件并super.equals在最后调用。因为superis Object,它总是会失败。

考虑:

trait T { override def equals(z: Any):Boolean = super.equals(z)} }

case class A(a: Int) extends T

val a = A(1); val b = A(1)

a==b // false

因此,如果您声明

case class Record(id: Long, name: String ...) extends KeyedEntity[Long] { ... }

- 你创建了几个Record实例但不持久化它们,它们的比较会中断。我通过为同一个类实现两个SalatSqueryl后端发现了这一点,然后所有Salat测试都失败了,因为isPersistedfromKeyedEntity是假的。

如果混合到一个案例类中,是否有一种设计KeyedEntity可以保持案例类的平等?我尝试将案例类类型为 P 的自键入和参数化BetterKeyedEntity[K,P] { self: P => ... },但它会导致 equals 中的无限递归。

就目前的情况而言,superObject覆盖的 equals in 的最终分支KeyedEntity将始终返回 false。

4

1 回答 1

2

equals如果有覆盖,通常为案例类生成的结构相等检查似乎不会生成。但是,必须注意一些细微之处。

将基于 id 的平等概念混入结构平等可能不是一个好主意,因为我可以想象它可能会导致细微的错误。例如:

  • x: A(1)y: A(1)还没有持久化,所以它们是相等的
  • 然后它们会被持久化,并且由于它们是单独的对象,持久性框架可能会将它们作为单独的实体持久化(我不知道 Squeryl,那里可能不是问题,但这是一条细线)
  • 在坚持之后,由于id不同,它们突然不相等。

更糟糕的是,如果xy被持久化到相同的 id,那么hashCode在持久化之前和之后会有所不同(来源显示,如果持久化它是 id 的 hashCode)。这破坏了不变性,并会导致非常糟糕的行为(例如,当放在地图中时)。请参阅我在其中演示断言失败的要点

所以不要隐式混合结构和基于 id 的平等。另请参阅在Hibernate的上下文中对此进行的解释。

类型类

必须注意的是,其他人指出(需要参考)基于方法的平等的概念是有缺陷的,出于这些原因(两个事物相等的方法不止一种)。因此,您可以定义一个描述 Equality 的类型类:

trait Eq[A] {
  def equal(x: A, y: A): Boolean
}

并为您的类定义(可能是多个)该类型类的实例:

// structural equality
implicit object MyClassEqual extends Eq[MyClass] { ... }

// id based equality
def idEq[K, A <: KeyedEntity[K]]: Eq[A] = new Eq[A] {
  def equal(x: A, y: A) = x.id == y.id
}

那么你可以请求事物是 Eq 类型类的成员:

def useSomeObjects[A](a: A, b: A)(implicit aEq: Eq[A]) = {
  ... aEq.equal(a, b) ...
}

因此,您可以通过在范围内导入适当的类型类或直接传递类型类实例来决定使用哪个相等概念,如useSomeObjects(x, y)(idEq[Int, SomeClass])

请注意,您可能还需要一个Hashable类型类,类似地。

自动生成 Eq 实例

这种情况与 Scala 标准库的类型类非常相似scala.math.Ordering。这是使用出色的无形自动派生Ordering案例类的结构实例的示例。

Eq对于和也很容易做到这一点Hashable

斯卡拉兹

请注意,scalaz 具有 typeclass Equal具有很好的 pimp 模式,您可以使用它来编写x === y而不是eqInstance.equal(x, y). 我还不知道它有Hashable类型类。

于 2012-09-15T07:26:19.027 回答