Convertir Scala Any a objeto Java
reflection scala-java-interop (2)
Tengo un problema al utilizar Java Reflection desde Scala. Mi código:
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))
Mi problema es que necesito pasar un Mapa [String, Any], porque uno de los valores es Double, pero newInstance necesita Object, no Any.
Intenté lo mismo con el universo scalas:
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))
El problema aquí es que solo presenté MyClass para el ejemplo. Más adelante debería ser una función genérica como def create(values: Map[String, Any]): T
Pero luego obtuve la siguiente excepción: "No TypeTag disponible para T"
¿Hay alguna forma de transformar estos valores?
Gracias
Ok, llegué un poco tarde pero aquí va:
Los siguientes trabajos:
constructor.newInstance(arguments.asInstanceOf[Array[AnyRef]]: _*).asInstanceOf[MyClass]
Ver también: Transformar Scala varargs en Java Object ... varargs
Consejo: Sería muy cauto con la reflexión. En Scala eso es mal estilo. Una forma de limitarlo / encapsularlo podría ser:
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))
java.lang.Object
es equivalente a AnyRef
en Scala, no a Any
. La idea es que Scala Double
(aproximadamente equivalente a Java double
) es un Any
, pero no un AnyRef
. java.lang.Double
es un AnyRef
, por lo tanto también un Any
.
Simplemente puedes lanzar un Any
a AnyRef
, que realizará la conversión necesaria para convertir un Double
Scala en un 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