斯卡拉地图到任意类(斯卡拉反射)

时间:2014-07-08 08:25:05

标签: scala reflection deserialization scala-collections

如果我有Scala地图

val map = Map("a" -> true,
              "b" -> "hello"
              "c" -> 5)

有没有办法可以将它转换为对象(case类,常规类,真正的任何东西),以便我可以像这样访问字段:

val obj = map.toObj
println(obj.a)
println(obj.b)

没有知道参数会提前是什么?

或者甚至更好,它可以是我正在使用的类的实例变量吗?

所以我真的可以

println(a)
println(b)

2 个答案:

答案 0 :(得分:2)

查看Dynamic

import scala.language.dynamics

class Wrapper(m: Map[String, Any]) extends Dynamic {
  def selectDynamic(name: String) = {
    m(name)
  }
}

object Demo {
  def main(args: Array[String]) {
    val map = Map("a" -> true,
      "b" -> "hello",
      "c" -> 5)

    val w = new Wrapper(map)
    println(w.a)
    println(w.b)
  }
}

Dynamic为您提供“按需提供的属性”。

但它不是类型安全的。在上面的代码段中,如果您尝试访问地图中的不存在的密钥,则会引发NoSuchElementException。并且您获得了Any类型,因此您必须在呼叫方旁使用asInstanceOf

Dynamic还有其他方法,因此请仔细查看Dynamic

的文档

答案 1 :(得分:2)

我希望您通过提出这个问题来理解,当您依赖运行时数据时,您将失去所有编译时保证。如果这真的是你想要的那么你可以依靠Dynamic

object X extends App {

  import scala.language.dynamics
  class D(fields: Map[String, Any]) extends Dynamic {
    def selectDynamic(str: String): Any =
      fields.getOrElse(str, throw new NoSuchFieldException(str))
  }

  val fields = Map[String, Any]("a" -> true, "b" -> "hello")
  val obj = new D(fields)

  println(obj.a)
  println(obj.b)
}

Dynamic是一种编译器功能,可将所有字段/方法调用转换为对*Dynamic*方法的调用。因为编译器不能了解你的程序(它应该怎么做?),你在这里得到的返回类型是Any,当你调用一个不存在的字段/方法时,你会在运行时得到一个异常而不是编译时错误。当已知编译时的某些字段/方法时,您可以将Dynamic与宏组合以至少进行一些编译时检查(这种方法在链接的答案中描述)。

除此之外,这是您可以在Scala中启用的唯一语法。如果返回类型对您很重要,您至少可以将它们添加为类型参数:

object X extends App {

  import scala.language.dynamics
  class D(fields: Map[String, Any]) extends Dynamic {
    def selectDynamic[A : reflect.ClassTag](str: String): A =
      fields.get(str) match {
        case Some(f: A) => f
        case _          => throw new NoSuchFieldException(str)
      }
  }

  val fields = Map[String, Any]("a" -> true, "b" -> "hello")
  val obj = new D(fields)

  println(obj.a[Boolean])
  println(obj.b[String])
}