Проверка scala, если хотя бы одно значение поля класса дела не пустое с использованием отражения
У меня есть требование, чтобы для данного класса дел с более чем 30+ полями Option[T] было необходимо по крайней мере 1 поле nonEmpty, чтобы быть действительным. Вместо того, чтобы проверять каждое поле в отдельности, я выбрал общий способ проверки всех полей с помощью отражения. Код, который я придумал (также основанный на некоторых других ответах в SO):
import scala.reflect.runtime.universe._
import scala.reflect.runtime.{universe => ru}
// gets all methods of a Case Class
def getMethods[T: ru.TypeTag] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
/**
* Returns the value of all Case Class fields
* @param obj case class object
* @return a Sequence of all field values
*/
def getAllCaseClassFieldValues[T: ru.TypeTag](obj: Object): Seq[Any] = {
val mirror = ru.runtimeMirror(getClass.getClassLoader)
getMethods[T].map(m => mirror.reflect(obj).reflectField(m).get)
}
Класс дела:
case class SampleRequest(field1: Option[String], field2: Option[String], //.... up to 30 or more fields
Код, который проверяет, является ли хотя бы 1 не пустым:
val fieldValues = getAllCaseClassFieldValues[SampleRequest](r)
val noneCount = fieldValues.count(_ == None)
val atLeastOneNonEmpty = noneCount < fieldValues.size
Мне было интересно, есть ли лучший способ проверить это с помощью рефлексии или другого механизма?
2 ответа
Это класс case, поэтому он реализует свойство Product. Признак продукта имеет итератор def productIterator : Iterator[Any]
case class Foo(one: Option[Int], two: Option[String], three : Option[Double])
var i = Foo(None, Some("Woot!"), None)
var j = Foo(Some(1), Some("There Can Be Only"), None)
var k = Foo(None, None, None)
i.productIterator.exists(_.isInstanceOf[Some[_]]) // True
j.productIterator.exists(_.isInstanceOf[Some[_]]) // True
k.productIterator.exists(_.isInstanceOf[Some[_]]) // False
Я использую этот прием все время при оценке малонаселенных потоковых данных.
Вы можете попробовать это:
abstract class AtLeastOneNonEmptyOption[T] { self: T with Product =>
def nonEmpty: Boolean = this.productIterator.exists {
case Some(_) => true
case _ => false
}
}
case class MyClass(foo: Option[String], bar: Option[Int]) extends AtLeastOneNonEmptyOption[MyClass]
val nonEmptyClass = MyClass(Some("foo"), None)
val emptyClass = MyClass(None, None)
nonEmptyClass.nonEmpty //true
emptyClass.nonEmpty //false
Там может быть лучший способ.