instantiate object with reflection using constructor arguments

15,538

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 of Object, so you should give the type inferer a hint with :_*

  • newInstance(...) returns an Object so you have to cast it back with asInstanceOf[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)
Share:
15,538

Related videos on Youtube

justin
Author by

justin

Updated on June 04, 2022

Comments

  • justin
    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
    Mitch Blevins over 14 years
    Does 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
    justin over 14 years
    Figuring 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
    Aamir almost 7 years
    how 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
    michael.kebe almost 7 years
    @Aamir What about java.lang.Class.forName("Foo")?