[英]Structured equals at runtime in Scala
Is there some function in scala that will compare two objects structurally at runtime? scala中是否有一些函数可以在运行时从结构上比较两个对象? I'd rather not have to manually create an equals function due to the number of fields. 由于字段数量的原因,我不必手动创建等于函数。 This is for a unit test, so performance isn't an issue. 这是一个单元测试,因此性能不是问题。 I just need something that reflectively looks through the fields and compares the values. 我只需要一些反射性地透过字段并比较值的东西。
Background: I have a case class that extends from a trait that overrides equals and thus doesn't generate the structural equals (squeryl KeyedEntity, related: Prevent Mixin overriding equals from breaking case class equality ), and I'm not in a position to change that right now. 背景:我有一个案例类,它从一个覆盖等于的特征延伸,因此不会生成结构等于(squeryl KeyedEntity,相关: 防止Mixin重写等于破坏案例类相等 ),我不能够现在改变它。 This is for a unit test so both instances are not persisted. 这是针对单元测试的,因此两个实例都不会保留。
AFAIK the Scala standard library does not contain such a function. AFAIK Scala标准库不包含这样的功能。 But you have two options. 但是你有两种选择。
I came up with this solution which iterates through all fields, and for private fields, finds a corresponding method in scala. 我想出了这个迭代遍历所有字段的解决方案,对于私有字段,在scala中找到相应的方法。 Indeed, case classes have private members accessible through methods of the same name. 实际上,案例类的私有成员可以通过相同名称的方法访问。
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
You can even make it more recursive by changing the last == in the method ReallyEqual
to ==:== 您甚至可以通过将方法ReallyEqual
为==:==来使其更加递归
Careful : This will not work for integers, because Int does not have any field or methods. 小心 :这对整数不起作用,因为Int没有任何字段或方法。 Ex: 1 ==:== 2 will return true. 例:1 ==:== 2将返回true。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.