I've a problem using Java Reflection from Scala. My Code:
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))
My problem is, that I need to pass a Map[String, Any], because one of the values is a Double, but newInstance needs Object, not Any.
I tried the same with 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))
Problem here is, that I only introduced MyClass for the example. Later it should be a generic function like def create(values: Map[String, Any]): T
. But then I got the following exception: "No TypeTag available for T"
Is there any way to transform these values?
Thank you
Decompile Scala code to Java In the Project tool window, right-click a Scala library class that you want to decompile. From the context menu, select Decompile Scala to Java. IntelliJ IDEA converts code to Java and opens the converted file in the editor.
AnyRef represents reference types. All non-value types are defined as reference types. Every user-defined type in Scala is a subtype of AnyRef . If Scala is used in the context of a Java runtime environment, AnyRef corresponds to java. lang.
Classes in Scala are blueprints for creating objects. They can contain methods, values, variables, types, objects, traits, and classes which are collectively called members.
java.lang.Object
is equivalent to AnyRef
in Scala, not Any
. The idea is, Scala Double
(roughly equivalent to Java double
) is an Any
, but not an AnyRef
. java.lang.Double
is an AnyRef
, thus also an Any
.
You can simply cast an Any
to AnyRef
, which will perform the needed conversion to turn a Scala Double
into a 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
Ok, I was a bit late but here goes:
The following works:
constructor.newInstance(arguments.asInstanceOf[Array[AnyRef]]: _*).asInstanceOf[MyClass]
See also: Transforming Scala varargs into Java Object... varargs
Advice: I'd be very cautious using reflection. In Scala that is bad style. One way to limit/encapsulate it could be:
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))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With