Here below is how to create a new instance of type T at runtime with Manifest
:
trait MyTrait
class MyClass1(val name: String) extends MyTrait
class MyClass2(val name: String) extends MyTrait
class Test[T <: MyTrait] {
def createInstance[T](name: String)(implicit m: Manifest[T]): T = {
m.runtimeClass.getConstructors()(0)
.newInstance(name).asInstanceOf[T]
}
def doSomething() {
val myClass = createInstance("joe")
...
}
}
...
val test = new Test[MyClass1]
test.doSomething
The createInstance
method above creates a new instance of one of the classes that implement MyTrait
and invokes the constructor with the given string. How do I implement the same with TypeTag
?
Reimplemented using scala.reflect.runtime._
Here below is class Test
reimplemented as suggested by som-snytt:
class Test[T <: MyTrait] {
import scala.reflect.runtime._
import scala.reflect.runtime.universe._
def createInstance[T: TypeTag](name: String): T = {
val tt = typeTag[T]
currentMirror.reflectClass(tt.tpe.typeSymbol.asClass).reflectConstructor(
tt.tpe.members.filter(m =>
m.isMethod && m.asMethod.isConstructor
).iterator.next.asMethod
)(name).asInstanceOf[T]
}
}
As long as I call createInstance
on Test
it works:
val test = new Test[MyClass1]
val myClass = test.createInstance("hello") // this works
But as soon as I define a new class derived from Test
...
class DerivedTest[T <: MyTrait] extends Test[T]
... and I invoke createInstance
on that new class like this...
val test = new DerivedText[MyClass1]
val myClass = test.createInstance("hello") // this crashes
... I get the following error:
java.util.NoSuchElementException: next on empty iterator
at scala.collection.Iterator$$anon$2.next(Iterator.scala:39)
at scala.collection.Iterator$$anon$2.next(Iterator.scala:37)
at scala.collection.LinearSeqLike$$anon$1.next(LinearSeqLike.scala:62)
at DerivedTest$class.createInstance(<console>:27)
at $anon$1.createInstance(<console>:26)
at .<init>(<console>:28)
at .<clinit>(<console>)
at .<init>(<console>:7)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
...
Am I missing something?
This may end up with your problem
j3d,
I believe the manifest approach is deprecated. You can use scala code below to create an instance via reflection. You will have to adjust the code slightly to factor for classes with multiple constructors, and/or arguments
Code is based on http://docs.scala-lang.org/overviews/reflection/overview.html
Eventually here is the implementation that actually works - please just assume this answer comes from som-snytt:
I hope that helps.
Maybe someone who does this all the time can chime in, but it took me this many steps to reproduce it.
I start the REPL -i, so autocomplete is broken. That puts me at a disadvantage.