Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

scala - instantiate object with reflection using constructor arguments

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...