我有一个如下所示的case类,只是它有更多的参数(都是可选的):
case class MyClass(i: Option[String], j: Option[Int], n: Option[OtherClass])
我想循环参数并验证是否只定义了i和j,而其余的都是None。如果case类在将来发生变化,我想保持相同的验证,这就是为什么我不使用模式匹配或显式检查。在scala 2.12中有什么好的方法可以做到这一点呢?
pxq42qpu1#
你可以像@Tim解释的那样使用productIterator,但是这听起来有点糟糕。如果有一个特殊的情况,只有MyClass(Some(_), Some(_), None)是有效的,而MyClass(Some(_), Some(_), Some(_))是无效的,那么它们应该是两个不同的类型,而不是一个“包罗万象”的类,在旁边处理ad-hoc验证。考虑一下这样的事情:
productIterator
MyClass(Some(_), Some(_), None)
MyClass(Some(_), Some(_), Some(_))
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" }
kuhbmx9i2#
您可以使用productIterator(case class的字段列表)执行此操作:
case 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 } }
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) }
这是假设productIterator和universe.typeOf[].decls以相同的顺序返回参数及其值。来自@Dima:Reflection是一种“元工具”,你会因为特定的原因(通常是在创建低级库的时候)去解决scala标准工具集没有提供的特定问题。
universe.typeOf[].decls
3条答案
按热度按时间pxq42qpu1#
你可以像@Tim解释的那样使用
productIterator
,但是这听起来有点糟糕。如果有一个特殊的情况,只有MyClass(Some(_), Some(_), None)
是有效的,而MyClass(Some(_), Some(_), Some(_))
是无效的,那么它们应该是两个不同的类型,而不是一个“包罗万象”的类,在旁边处理ad-hoc验证。考虑一下这样的事情:
这样,您只需检查正在处理的对象的类型,即可区分这两种情况:
kuhbmx9i2#
您可以使用
productIterator
(case class
的字段列表)执行此操作:然而这种泛型代码有风险,所以我建议避免使用包含大量字段的
case class
,考虑将不同的字段分组到它们自己的对象中,并对已知字段进行显式测试。当字段数量发生变化时更新测试并不是一件很麻烦的事情,这是一个检查这些变化是否影响了您的代码的机会。
原始版本使用方法:
xn1cxnb43#
我发现你可以通过反射来实现这一点,尽管这可能不被推荐参见下面的注解:
这是假设
productIterator
和universe.typeOf[].decls
以相同的顺序返回参数及其值。来自@Dima:Reflection是一种“元工具”,你会因为特定的原因(通常是在创建低级库的时候)去解决scala标准工具集没有提供的特定问题。