instantiate object with reflection using constructor arguments
Solution 1
scala> case class Foo(id:Long, name:String)
defined class Foo
scala> val constructor = classOf[Foo].getConstructors()(0)
constructor: java.lang.reflect.Constructor[_] = public Foo(long,java.lang.String)
scala> val args = Array[AnyRef](new java.lang.Integer(1), "Foobar")
args: Array[AnyRef] = Array(1, Foobar)
scala> val instance = constructor.newInstance(args:_*).asInstanceOf[Foo]
instance: Foo = Foo(1,Foobar)
scala> instance.id
res12: Long = 1
scala> instance.name
res13: String = Foobar
scala> instance.getClass
res14: java.lang.Class[_] = class Foo
Currently there is not much reflection support in Scala. But you can fall back to th Java Reflection API. But there are some obstacles:
You have to create a
Array[AnyRef]
and box your "primitive types" in the wrapper classes (java.lang.Integer, java.lang.Character, java.lang.Double, ...
)newInstance(Object ... args)
gets an varargs array ofObject
, so you should give the type inferer a hint with:_*
newInstance(...)
returns anObject
so you have to cast it back withasInstanceOf[T]
The closest I could get to your instantiate
function is this:
def instantiate(clazz: java.lang.Class[_])(args:AnyRef*): AnyRef = {
val constructor = clazz.getConstructors()(0)
return constructor.newInstance(args:_*).asInstanceOf[AnyRef]
}
val instance = instantiate(classOf[MyClass])(new java.lang.Integer(42), "foo")
println(instance) // prints: MyClass(42,foo)
println(instance.getClass) // prints: class MyClass
You cannot get the get class from a generic type. Java erases it (type erasure).
Edit: 20 September 2012
Three years on, the instantiate
method can be improved to return a properly typed object.
def instantiate[T](clazz: java.lang.Class[T])(args:AnyRef*): T = {
val constructor = clazz.getConstructors()(0)
return constructor.newInstance(args:_*).asInstanceOf[T]
}
See http://www.nabble.com/How-do-I-get-the-class-of-a-Generic--td20873455.html
Solution 2
See answers to Scala: How do I dynamically instantiate an object and invoke a method using reflection? as well, especially regarding type erasure.
Solution 3
This is what I've ended up with so far, I'd like to not have to deal directly with AnyRef if possible. So if anyone knows a way to get around that I'd appreciate the help.
case class MyClass(id:Long,name:String)
def instantiate[T](classArgs: List[AnyRef])(implicit m : Manifest[T]) : T ={
val constructor = m.erasure.getConstructors()(0)
constructor.newInstance(classArgs:_*).asInstanceOf[T]
}
val list = List[AnyRef](new java.lang.Long(1),"a name")
val result = instantiate[MyClass](list)
println(result.id)
Related videos on Youtube
justin
Updated on June 04, 2022Comments
-
justin almost 2 years
I'm trying to figure out how to instantiate a case class object with reflection. Is there any support for this? The closest I've come is looking at scala.reflect.Invocation, but this seems more for executing methods that are a part of an object.
case class MyClass(id:Long, name:String) def instantiate[T](className:String)(args:Any*) : T = { //your code here }
Is close to the API I'm looking for.
Any help would be appreciated.
-
Mitch Blevins over 14 yearsDoes this really allow you to instantiate a class that you don't know about until runtime? Doesn't the type parameter to the instantiate method rule that out?
-
justin over 14 yearsFiguring this piece out was just a small part of what I'm doing. I'm just experimenting with ideas of using immutable objects in a data access layer.
-
Aamir almost 7 yearshow can we modify above program if we have input as string value of case class, i.e in above case say we have input to our program as string value of case class as "Foo".So how to construct runtime class based on string value of case class
-
michael.kebe almost 7 years@Aamir What about
java.lang.Class.forName("Foo")
?