scala 迭代case类中的参数和可选值,检查是否只定义了某些字段

0lvr5msh  于 2023-01-02  发布在  Scala
关注(0)|答案(3)|浏览(128)

我有一个如下所示的case类,只是它有更多的参数(都是可选的):

case class MyClass(i: Option[String], j: Option[Int], n: Option[OtherClass])

我想循环参数并验证是否只定义了i和j,而其余的都是None。如果case类在将来发生变化,我想保持相同的验证,这就是为什么我不使用模式匹配或显式检查。
在scala 2.12中有什么好的方法可以做到这一点呢?

pxq42qpu

pxq42qpu1#

你可以像@Tim解释的那样使用productIterator,但是这听起来有点糟糕。如果有一个特殊的情况,只有MyClass(Some(_), Some(_), None)是有效的,而MyClass(Some(_), Some(_), Some(_))是无效的,那么它们应该是两个不同的类型,而不是一个“包罗万象”的类,在旁边处理ad-hoc验证。
考虑一下这样的事情:

sealed trait Foo {
      def i: Option[String]
      def j: Option[Int]
      def n: Option[OtherClass] = None
   }

   case class IJ(iVal: String, jVal: Int) extends Foo {
      def i = Some(iVal)
      def j = Some(jVal) 
   }
   
   case class IJN(i: Option[String], j: Option[Int], nVal: OtherClass) extends Foo {
      override def n = Some(nVal)
   }

这样,您只需检查正在处理的对象的类型,即可区分这两种情况:

foo match { 
       case x: IJ => "i and j are defined, but n is not"
       case x: IJN => "N is defined, but i and j might not be"
    }
kuhbmx9i

kuhbmx9i2#

您可以使用productIteratorcase class的字段列表)执行此操作:

def verify(mc: MyClass) = {
  val c = mc.productIterator.count{
    case o: Option[_] => o.nonEmpty
      case _ => true
  }

  mc.i.nonEmpty && mc.j.nonEmpty && c == 2
}

然而这种泛型代码有风险,所以我建议避免使用包含大量字段的case class,考虑将不同的字段分组到它们自己的对象中,并对已知字段进行显式测试。
当字段数量发生变化时更新测试并不是一件很麻烦的事情,这是一个检查这些变化是否影响了您的代码的机会。
原始版本使用方法:

case class MyClass(i: Option[String], j: Option[Int], n: Option[OtherClass])
{
  def verify = {
    val c = this.productIterator.count{
      case o: Option[_] => o.nonEmpty
      case _ => true
    }
    i.nonEmpty && j.nonEmpty && c == 2
  }
}
xn1cxnb4

xn1cxnb43#

我发现你可以通过反射来实现这一点,尽管这可能不被推荐参见下面的注解:

val allowableFieldNames = Set("i", "j")   
val fields = universe.typeOf[MyClass].decls.collect {
   case m: MethodSymbol if m.isCaseAccessor => m.name.toString
 }.toList

val values = myClass.productIterator.toList

values.zip(fields).collect {
    case(o: Option[_], field: String) if !allowableFieldNames(field) => assert(o.isEmpty)  
}

这是假设productIteratoruniverse.typeOf[].decls以相同的顺序返回参数及其值。
来自@Dima:Reflection是一种“元工具”,你会因为特定的原因(通常是在创建低级库的时候)去解决scala标准工具集没有提供的特定问题。

相关问题