2013-10-04 2 views
12

Есть ли какая-то функция в scala, которая будет структурно сравнивать два объекта во время выполнения? Я бы предпочел не создавать вручную равную функцию из-за количества полей. Это для единичного теста, поэтому производительность не является проблемой. Мне просто нужно что-то, что отражательно просматривает поля и сравнивает значения.Структурированные равны во время выполнения в Scala

Фон: у меня есть класс case, который простирается от признака, который переопределяет равные и, следовательно, не генерирует структурные равные (squeryl KeyedEntity, связанные: Prevent Mixin overriding equals from breaking case class equality), и я не в состоянии изменить это прямо сейчас , Это для единичного теста, поэтому оба экземпляра не сохраняются.

+2

В объектах классов case вы можете использовать 'productIterator.toList', если вас интересуют только значения класса case. – pedrofurla

+0

продуктIterator.toList не работает. Это просто помещает экземпляр в список. – ttt

+0

Отразите оба объекта и сравните их поля и значения. – Ashalynd

ответ

1

Я придумал это решение, которое выполняет итерацию через все поля, а для частных полей находит соответствующий метод в scala. Действительно, классы case имеют частные члены, доступные с помощью методов с тем же именем.

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 в Scala не содержит такой функции. Но у вас есть два варианта.

  1. Вы пишете макрос, который генерирует метод для сравнения равенства двух заданных значений
  2. Использование Java отражения, если производительность не имеет значения. См. Этот ответ: Is there a Java reflection utility to do a deep comparison of two objects?
+1

Если вы пишете такой макрос, этот ответ может выиграть щедрость. –

+4

3. Используйте отражение Скалы. –

Смежные вопросы