10

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.

Jim Ferrans
  • 30,582
  • 12
  • 56
  • 83
justin
  • 453
  • 1
  • 4
  • 13
  • The reflection involved here has nothing to do with case classes specifically. You just need some reflection to typecast the args. If I am wrong, can you elaborate? – HRJ Oct 29 '09 at 05:53
  • You are correct, except that part of the goal is to be able to write simple case classes and then use a method like this to instantiate/modify them. – justin Oct 29 '09 at 12:39

3 Answers3

22
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

kiritsuku
  • 52,967
  • 18
  • 114
  • 136
michael.kebe
  • 10,916
  • 3
  • 44
  • 62
  • 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 – Aamir Jun 08 '17 at 11:47
  • @Aamir What about `java.lang.Class.forName("Foo")`? – michael.kebe Jun 08 '17 at 20:28
3

See answers to Scala: How do I dynamically instantiate an object and invoke a method using reflection? as well, especially regarding type erasure.

Community
  • 1
  • 1
Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
2

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)
justin
  • 453
  • 1
  • 4
  • 13
  • 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? – Mitch Blevins Oct 29 '09 at 15:43
  • 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. – justin Oct 29 '09 at 16:59