【问题标题】:Scala getting field and type of field of a case classScala获取案例类的字段和字段类型
【发布时间】:2014-12-17 11:52:37
【问题描述】:

所以我试图在案例类中获取字段及其类型。目前我正在这样做

typeOf[CaseClass].members.filter(!_.isMethod).foreach{
   x =>
     x.typeSignature match {
        case _:TypeOfFieldInCaseClass => do something
        case _:AnotherTypeOfFieldInCaseClass => do something
     }
}

问题是x.typeSignaturereflect.runtime.universe.Type 类型,它不能匹配任何驻留在案例类中的类型。有没有办法做到这一点?

【问题讨论】:

    标签: scala reflection pattern-matching match case-class


    【解决方案1】:

    假设您定义了以下案例类:

    case class CaseClass(i: Int, s: String)
    

    通过模式匹配,您可以通过以下方式实现您想要的:

    import scala.reflect.runtime.universe._
    
    typeOf[CaseClass].members.filter(!_.isMethod).map(_.typeSignature).foreach {
      case t if t == typeOf[Int] => print("i")
      case s if s == typeOf[String] => print("s")
    }
    

    为什么?

    那么为什么第一次尝试不起作用?

    那是因为在您的代码中使用了类型模式。类型模式在运行时检查要匹配的主题的类型 - 在这种情况下是类型签名。因此,通过使用_: Int,我们要求在运行时针对CaseClass 的非方法成员的每个类型签名进行类型检查。

    但在这种情况下,我们需要的是值匹配。

    让我们仔细看看(使用 Scala REPL):

    scala> case class CaseClass(i: Int, s: String)
    defined class CaseClass
    
    scala> typeOf[CaseClass]
    res1: reflect.runtime.universe.Type = CaseClass
    
    scala> typeOf[CaseClass].members
    res2: reflect.runtime.universe.MemberScope = Scopes(method equals, method toString, ..)
    
    scala> typeOf[CaseClass].members.filter(!_.isMethod)
    res4: Iterable[reflect.runtime.universe.Symbol] = SynchronizedOps(value s, value i)
    
    scala> typeOf[CaseClass].members.filter(!_.isMethod).map(_.typeSignature)
    res5: Iterable[reflect.runtime.universe.Type] = List(String, scala.Int)
    

    所以我们要匹配的是reflect.runtime.universe.Type 类型。请注意,在最后一行中,Stringscala.Int 只是这些类型的字符串表示形式,而不是它们的实际类型!

    所以我们需要将它们与这种类型的不同值进行匹配,我们可以通过typeOf[Int]typeOf[String] 轻松获得。

    更多关于 Scala 中的模式匹配

    您可能希望使用以下代码使代码更简洁:

    typeOf[CaseClass].members.withFilter(!_.isMethod).map(_.typeSignature).foreach {
      case typeOf[Int] => print("i")    // Won't Compile!
      case typeOf[Int] => print("s")    // Won't Compile
    }
    

    但这会给你以下编译错误:

    not found: type typeOf
    

    这也是因为我们需要匹配一个以大写字母开头的变量名。所以以下工作:

    val T = typeOf[Int]
    val S = typeOf[String]
    
    typeOf[CaseClass].members.withFilter(!_.isMethod).map(_.typeSignature).foreach {
      case T => print("i")
      case S => print("s")
    }
    

    有关模式匹配的更多详细信息,请参阅Programming in Scala。您将在此处找到模式匹配的详细说明。

    【讨论】:

    • 如果字段是选项,这将如何改变?。如果字段是选项,则上述方法不起作用并导致匹配错误。
    • 在这种情况下,你需要一个额外的守卫(额外的case),否则你会得到一个MatchError
    • 无论如何,这是对您在问题中提出的问题的回答,但我建议您查看您的要求并考虑其他替代方案。我想说的是,在大多数情况下,您无需反射即可实现相同的功能,除非您正在编写框架。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-03
    • 1970-01-01
    • 2020-11-27
    • 1970-01-01
    • 2013-04-11
    • 2016-09-07
    相关资源
    最近更新 更多