scala中是否有一些函数可以在运行时在结构上比较两个对象?由于字段的数量,我宁愿不必手动创建 equals 函数。这是一个单元测试,所以性能不是问题。我只需要一些能够反思性地查看字段并比较值的东西。
背景:我有一个 case 类,它从覆盖 equals 的特征扩展而来,因此不会生成结构 equals (squeryl KeyedEntity,相关:防止 Mixin 覆盖 equals 从破坏案例类平等),我无法立即更改。这是针对单元测试的,因此两个实例都不会持久化。
scala中是否有一些函数可以在运行时在结构上比较两个对象?由于字段的数量,我宁愿不必手动创建 equals 函数。这是一个单元测试,所以性能不是问题。我只需要一些能够反思性地查看字段并比较值的东西。
背景:我有一个 case 类,它从覆盖 equals 的特征扩展而来,因此不会生成结构 equals (squeryl KeyedEntity,相关:防止 Mixin 覆盖 equals 从破坏案例类平等),我无法立即更改。这是针对单元测试的,因此两个实例都不会持久化。
AFAIK Scala 标准库不包含这样的功能。但是你有两个选择。
我想出了这个遍历所有字段的解决方案,对于私有字段,在 scala 中找到了相应的方法。实际上,案例类具有可通过同名方法访问的私有成员。
implicit class ReallyEqual[T](a: T) {
import java.lang.reflect.Modifier._
def ==:==[U](b: U): Boolean = {
val fieldsA = a.getClass.getDeclaredFields
val methodsA = a.getClass.getDeclaredMethods
val mapA = (methodsA.toList map (z => (z.getName(), z))).toMap
val fieldsB = b.getClass.getDeclaredFields
val methodsB = b.getClass.getDeclaredMethods
val mapB = (methodsB.toList map (z => (z.getName(), z))).toMap
fieldsA.length == fieldsB.length &&
(true /: (fieldsA zip fieldsB)){ case (res, (aa, bb)) =>
if(((aa.getModifiers & STATIC) != 0) || ((bb.getModifiers & STATIC) != 0)) { res // ignore
} else if(((aa.getModifiers & (PRIVATE | PROTECTED)) == 0) && ((bb.getModifiers & (PRIVATE | PROTECTED)) == 0)) {
res && aa == bb && aa.get(a) ==:== bb.get(b)
} else if((mapA contains aa.getName) && (mapB contains bb.getName)) {
res && mapA(aa.getName).invoke(a) == mapB(aa.getName).invoke(b)
} else res
}
}
}
trait EqualBad {
override def equals(other: Any) = false
}
case class MyCaseClass(x: Int, y: List[Int]) extends EqualBad
MyCaseClass(1, List(1, 2)) ==:== MyCaseClass(1, List(1, 2)) // true
MyCaseClass(1, List(1, 2)) ==:== MyCaseClass(1, List(2, 2)) // false
MyCaseClass(1, List(1, 2)) ==:== MyCaseClass(2, List(1, 2)) // false
MyCaseClass(1, List(1, 2)) == MyCaseClass(1, List(1, 2)) // false
ReallyEqual
您甚至可以通过将方法中的最后一个 == 更改为 ==:== 来使其更具递归性
小心:这不适用于整数,因为 Int 没有任何字段或方法。例如:1 ==:== 2 将返回 true。