Структурированные равны во время выполнения в Scala

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

Предыстория: у меня есть класс case, который простирается от признака, который переопределяет равные и, следовательно, не генерирует структурные равные (squeryl KeyedEntity, связанный: Предотвращение перекрытия Mixin равным от разлома классовое равенство), и я не в состоянии изменить это прямо сейчас. Это для unit test, поэтому оба экземпляра не сохраняются.

Ответ 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

Вы даже можете сделать его более рекурсивным, изменив последнее value == в методе ReallyEqual на ==: ==

Осторожно. Это не будет работать для целых чисел, потому что у Int нет поля или методов. Пример: 1 ==: == 2 вернет true.

Ответ 2

AFAIK в стандартной библиотеке Scala не содержит такой функции. Но у вас есть два варианта.