【问题标题】:Getting field name and types from Case Class (with Option)从案例类中获取字段名称和类型(带选项)
【发布时间】:2014-10-27 06:14:33
【问题描述】:

假设我们有一个模型,表示为case class,这样

case class User(firstName:String,lastName:String,age:Int,planet:Option[Planet])

sealed abstract class Planet
case object Earth extends Planet
case object Mars extends Planet
case object Venus extends Planet

本质上,通过使用反射或宏,能够获取用户案例类的字段名称,以及字段表示的类型。这也包括Option,即在提供的示例中,需要能够区分Option[Planet]Planet

在 scala'ish 伪代码中,类似这样的

val someMap = createTypedMap[User] // Assume createTypedMap is some function which returns map of Strings to Types
someMap.foreach{case(fieldName,someType) {
   val statement = someType match {
       case String => s"$fieldName happened to be a string"
       case Int => s"$fieldName happened to be an integer"
       case Planet => s"$fieldName happened to be a planet"
       case Option[Planet] => s"$fieldName happened to be an optional planet"
       case _ => s"unknown type for $fieldName"

   }
   println(statement)

}

我目前知道您不能执行 case Option[Planet] 之类的操作,因为它会被 Scala 擦除,但是即使使用 TypeTags,我也无法编写执行我想做的代码的代码,并可能处理其他类型(如Either[SomeError,String])。

目前我们使用的是最新版本的 Scala (2.11.2),因此任何使用 TypeTagsClassTags 或宏的解决方案都绰绰有余。

【问题讨论】:

标签: scala generics reflection macros scala-macros


【解决方案1】:

Option 是一个类型参数化的类型(Option[T])。在运行时,除非您已将代码结构化为使用type tags,否则由于类型擦除(所有类型参数化类型都是如此),您无法区分 Option[String] 和 Option[Int]。 尽管如此,您还是可以区分 Option[*] 和 Planet。请记住第一个问题。

通过反射,获取类中的所有“事物”很容易。例如,假设您只想要 getter(您可以放置​​其他类型的过滤器,它们有很多,当继承是过程的一部分时,并非所有过滤器都按预期运行,因此您需要进行一些试验):

import reflect.runtime.{universe=>ru}
val fieldSymbols = ru.typeOf[User].members.collect{ 
    case m: ru.MethodSymbol if m.isGetter => m 
}

如果您在实例而不是类上调用代码,您还有一个选择是遍历每个方法,调用该方法并将结果分配给一个变量,然后测试变量的类型。这假设您只调用不改变实例状态的方法。

您有很多选择,有时间找到最适合您需求的选择。

【讨论】:

  • 似乎最好的选择是使用宏(或一些实现宏的库)。由于指出了类型擦除问题,尝试通过检查 getter/setter 来检测 Option 远非理想(它也很容易在 Scala 版本之间中断)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-17
  • 1970-01-01
  • 2020-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多