Squeryl定义了一个特征KeyedEntity,它覆盖了equals,检查if中的几个条件,最后调用super.equals.由于super是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实例但不保留它们,它们的比较将会中断.我通过为同一个类实现Salat和Squeryl后端找到了这个,然后所有Salat测试失败,因为来自KeyedEntity的isPersisted是错误的.
是否存在KeyedEntity如果混合到案例类中将保持案例类相等的设计?我尝试了自我键入和参数化BetterKeyedEntity [K,P] {self:P => …}将case类类型作为P,但它会导致equals中的无限递归.
就像现在的情况一样,super是Object,因此KeyedEntity中被覆盖的equals的最终分支将始终返回false.
最佳答案 如果存在等于覆盖,则通常不会生成通常为案例类生成的结构相等性检查.然而,必须注意一些细微之处.
将基于身份的平等概念混淆回到结构平等可能不是一个好主意,因为我可以想象它可能会导致微妙的错误.例如:
> x:A(1)和y:A(1)尚未持久化,因此它们是相等的
>然后它们会被持久化,并且因为它们是独立的对象,持久性框架可能会将它们作为单独的实体保留(我不知道Squeryl,也许不是问题,但这是一条很细的行走)
>坚持之后,由于身份不同,他们突然不相等.
更糟糕的是,如果x和y持久化到相同的id,hashCode将在持久化之前和之后不同(the source表明如果持久化则它是id的hashCode).这打破了不变性,并将导致非常糟糕的行为(例如,当放入地图时).见this gist in which I demonstrate the assert failing.
所以不要隐含地混合结构和基于id的相等性.另请参阅context of 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 stdlib的scala.math.Ordering类型类.以下是auto-deriving structural Ordering
instances for case classes使用优秀shapeless库的示例.
Eq和Hashable也很容易做到这一点.
Scalaz
注意scalaz has Equal
typeclass,有很好的皮条客模式,你可以用它来写x === y而不是eqInstance.equal(x,y).我不知道它有Hashable类型类.