Reputation: 1584
I have a class hierarchy of Item
s that each need a corresponding ItemTemplate
instance as a constructor parameter. I'd like to write a generic function to instantiate any Item
subclass by giving it Item
and ItemTemplate
as type parameters, using it like this:
val newItem = instantiateItem[ItemClass, TemplateClass](templateInstance)
After a bit of research I now have
def instantiateItem[I, T](implicit mf: Manifest[I], template: T): I = {
val constructor = mf.erasure.getConstructor(classOf[T])
constructor.newInstance(template).asInstanceOf[I]
}
But this doesn't compile, the classOf[T]
gives the error
Class type required but T found
I tried replacing classOf[T]
with classManifest[CM].erasure
but this doesn't work as CM needs to be context bounded to ClassManifest
, and apparently it's not possible to use bounded type parameters with implicit parameters.
Is it possible to do what I want here?
Upvotes: 0
Views: 991
Reputation: 67310
You can get the template
class simply by calling template.getClass
. It requires template
to be a subtype of AnyRef
, so either you cast it to AnyRef
(forcing boxing of primitive types) or you add an upper bound for T
:
def instantiateItem[I, T <: AnyRef](implicit mf: Manifest[I], template: T): I = {
val constructor = mf.erasure.getConstructor(template.getClass)
constructor.newInstance(template).asInstanceOf[I]
}
If you want to pass in template
explicitly, as indicated by the code in your question, you need to separate implicit and explicit arguments, e.g.
def instantiateItem[I, T <: AnyRef](template: T)(implicit mf: Manifest[I]): I = {
val constructor = mf.erasure.getConstructor(template.getClass)
constructor.newInstance(template).asInstanceOf[I]
}
aka
def instantiateItem[I : Manifest, T <: AnyRef](template: T): I = {
val mf = implicitly[Manifest[I]]
val constructor = mf.erasure.getConstructor(template.getClass)
constructor.newInstance(template).asInstanceOf[I]
}
In general if possible you could avoid having to use reflection at all with careful design:
trait ItemCompanion[I,T] {
def instantiateItem(template: T): I
}
object TestItem extends ItemCompanion[TestItem, TestTemplate] {
implicit def self: ItemCompanion[TestItem, TestTemplate] = this
def instantiateItem(template: TestTemplate): TestItem = new TestItem(template)
}
class TestItem(template: TestTemplate)
trait TestTemplate
// try out
def instantiateItem[I, T](implicit ic: ItemCompanion[I, T], t: T): I =
ic.instantiateItem(t)
implicit val temp: TestTemplate = new TestTemplate {}
instantiateItem[TestItem, TestTemplate]
Upvotes: 1
Reputation: 43330
These corrections to your code should do the trick:
def instantiateItem[I : Manifest, T <: AnyRef : Manifest](template: T): I = {
val constructor = manifest[I].erasure.getConstructor(manifest[T].erasure)
constructor.newInstance(template).asInstanceOf[I]
}
The I : Manifest
syntax is a preferred sugarred version of your implicit parameter.
Please note that since Scala 2.10 Manifest
will be deprecated in favor of TypeTag
Upvotes: 1