我有一个问题,从Scala使用Java反射。我的代码:
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。
我对scala 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。之后它应该是一个像def create(values: Map[String, Any]): T
一样的泛型函数。但随后我得到了以下异常:"No TypeTag available for T"
是否有任何方法来转换这些值?
谢谢
java.lang.Object
在Scala中等同于AnyRef
,而不是Any
。其思想是,Scala Double
(大致相当于Java double
)是Any
,而不是AnyRef
。java.lang.Double
是AnyRef
,因此也是Any
。
您可以简单地将Any
转换为AnyRef
,它将执行所需的转换,将Scala Double
转换为java.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
好吧,我来晚了一点,但是现在开始:
以下作品:
constructor.newInstance(arguments.asInstanceOf[Array[AnyRef]]: _*).asInstanceOf[MyClass]
参见:将Scala变量转换为Java对象…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))