hezamu
hezamu

Reputation: 1584

Instantiating a type parameter without a no-arg constructor

I have a class hierarchy of Items 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

Answers (2)

0__
0__

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

Nikita Volkov
Nikita Volkov

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

Related Questions