2013-10-04 3 views
12

런타임에 두 객체를 구조적으로 비교하는 일부 함수가 스칼라에 있습니까? 차라리 필드 수로 인해 equals 함수를 수동으로 생성하지 않아도됩니다. 이것은 단위 테스트 용이므로 성능에 문제가 없습니다. 필드를 반사적으로 바라보고 값을 비교하는 무언가가 필요합니다.Scala에서 런타임에 구조화 된 같음

배경 : 나는 equals를 오버라이드하고 따라서 구조적 equals (squeryl KeyedEntity, related : Prevent Mixin overriding equals from breaking case class equality)를 생성하지 않는 특성을 확장하는 케이스 클래스를 가지고 있으며, 지금 당장이를 바꿀 수있는 위치에 있지 않습니다. . 이것은 단위 테스트 용이므로 두 인스턴스가 지속되지 않습니다.

+2

사례 클래스의 객체에서는 사례 클래스 값에만 관심이있는 경우 'productIterator.toList'를 사용할 수 있습니다. – pedrofurla

+0

productIterator.toList가 작동하지 않았습니다. 인스턴스를 목록에 넣으면됩니다. – ttt

+0

두 개체를 모두 반사시키고 필드와 값을 비교하십시오. – Ashalynd

답변

1

모든 필드를 반복하고 개인 필드의 경우 해당하는 방법을 스칼라로 찾는이 솔루션이 등장했습니다. 실제로 사례 클래스에는 동일한 이름의 메서드를 통해 액세스 할 수있는 private 멤버가 있습니다.

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를 반환합니다.

1

AFAIK 스칼라 표준 라이브러리에는 이러한 함수가 없습니다. 하지만 두 가지 옵션이 있습니다.

  1. 당신은 성능이 중요하지 않을 경우 주어진 두 값
  2. 를 사용하여 자바 반사의 평등을 비교하는 방법을 생성하는 매크로를 작성합니다. 이 답변을 참조하십시오 : Is there a Java reflection utility to do a deep comparison of two objects?
+1

그런 매크로를 작성하면이 응답으로 현상금이 발생할 수 있습니다. –

+4

3. 스칼라 반사를 사용합니다. –