从Case Class获取字段名称和类型(带选项)

时间:2014-10-27 06:14:34

标签: scala generics reflection macros scala-macros

假设我们有一个东西的模型,表示为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或宏的解决方案都绰绰有余。

2 个答案:

答案 0 :(得分:1)

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

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

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

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

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

答案 1 :(得分:-1)

这是一个很长的镜头,但也许Shapeless可以帮助你。