将 Scala Any 转换为 Java 对象

2022-09-02 03:19:10

我在使用Scala的Java Reflection时遇到了一个问题。我的代码:

case class MyClass(id: String, value: Double)     

def create(values: Map[String, Any]): MyClass = {
   val constructor = classOf[MyClass].getConstructors.head
   val arguments = classOf[MyClass].getDeclaredFields().map( f => values(f.getName) )
   constructor.newInstance(arguments: _*).asInstanceOf[MyClass]
} 

create(Map("id" -> "CE0D23A", "value" -> 828.32))

我的问题是,我需要传递一个Map[String,Any],因为其中一个值是Double,但newInstance需要Object,而不是Any。

我在scalas universe上尝试了同样的方法:

case class MyClass(id: String, value: Double)     

def create(values: Map[String, Any]): MyClass = {
   val m = universe.runtimeMirror(getClass.getClassLoader)
   val myClass = universe.typeOf[MyClass].typeSymbol.asClass
   val cm = m.reflectClass(myClass)
   val ctro = universe.typeOf[MyClass].declaration(universe.nme.CONSTRUCTOR).asMethod
   val ctorm = cm.reflectConstructor(ctro)
   ctorm(values: _*).asInstanceOf[MyClass]
} 

create(Map("id" -> "CE0D23A", "value" -> 828.32))

这里的问题是,我只为这个例子引入了MyClass。稍后它应该是一个泛型函数,如 .但后来我得到了以下异常:“没有可用于T的TypeTag”def create(values: Map[String, Any]): T

有没有办法转换这些值?

谢谢


答案 1

java.lang.Object等效于在 Scala 中,而不是 。这个想法是,Scala(大致相当于Java)是一个,但不是. 是 一个,因此也是一个 .AnyRefAnyDoubledoubleAnyAnyRefjava.lang.DoubleAnyRefAny

你可以简单地将一个 to 转换为 ,它将执行所需的转换,将 Scala 转换为 :AnyAnyRefDoublejava.lang.Double

scala> val x = 3.5
x: Double = 3.5

scala> x.getClass
res0: Class[Double] = double

scala> val y = x.asInstanceOf[AnyRef]
y: AnyRef = 3.5

scala> y.getClass
res1: Class[_ <: AnyRef] = class java.lang.Double

答案 2

好吧,我有点晚了,但这里是:

以下工作原理:

constructor.newInstance(arguments.asInstanceOf[Array[AnyRef]]: _*).asInstanceOf[MyClass]

参见:将 Scala varargs 转换为 Java Object...varargs

建议:我会非常谨慎地使用反射。在Scala中,这是糟糕的风格。限制/封装它的一种方法是:

case class MyClass(id: String, value: Double)     

object MyClass {

    import scala.util.Try
    def apply(map: Map[String, Any] /* this is asking for trouble */) : Option[MyClass]  =  for {
            id <- maybeT[String](map.get("id"))
            value <- maybeT[Double](map.get("value"))
        } yield MyClass(id, value)

    // a truly global utility?
    @inline def maybeT[T] ( a: Any /*Option[T]*/ ) : Option[T]= Try(a.asInstanceOf[Option[T]]).toOption.flatten //yep ugly as hell

}


MyClass(Map("id" -> "CE0D23A", "value" -> 828.32))