【问题标题】:Scala Map to Arbitrary Class (Scala Reflection)Scala 映射到任意类(Scala 反射)
【发布时间】:2014-07-08 21:26:37
【问题描述】:

如果我有 Scala 地图

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

有没有办法可以将它转换为一个对象(案例类、常规类、任何东西),这样我就可以像这样访问该字段:

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

不知道提前知道参数是什么?

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

所以我实际上可以拥有

println(a)
println(b)

【问题讨论】:

  • 你真的对参数一无所知吗?或者你会得到一组有限的选项之一吗?我不明白你怎么可能不知道参数名称然后期望去println(a)obj.a,因为这似乎需要你知道它是a

标签: scala reflection deserialization scala-collections


【解决方案1】:

看看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 为您提供“按需属性”。

但它不是类型安全的。在上面的 sn-p 中,如果您尝试访问映射中不存在的键,则会抛出 NoSuchElementException。你得到Any 类型,所以你必须在调用方使用asInstanceOf

Dynamic还有更进一步的方法,请仔细查看Dynamic的文档

【讨论】:

    【解决方案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])
    }
    

    【讨论】:

    • 当我运行你的最后一个例子时,我得到一个MatchError (2.10.3)。如果使用[java.lang.Boolean] 而不是[Boolean],它可以工作。有没有办法可以使用[Boolean]
    • 我修复了MatchError,但无法说明为什么普通的Boolean 参数在2.10 中不起作用。这似乎是一个错误,但我无法尝试,因为我这里没有 2.10 安装。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-14
    • 2021-12-02
    • 2019-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多