实例化没有no-arg构造函数的类型参数

时间:2012-08-11 15:31:47

标签: scala

我有一个Item的类层次结构,每个层次结构都需要一个对应的ItemTemplate实例作为构造函数参数。我想编写一个泛型函数来实例化任何Item子类,方法是将ItemItemTemplate作为类型参数,使用它如下:

val newItem = instantiateItem[ItemClass, TemplateClass](templateInstance)

经过一番研究后我才有了

def instantiateItem[I, T](implicit mf: Manifest[I], template: T): I = {
    val constructor = mf.erasure.getConstructor(classOf[T])
    constructor.newInstance(template).asInstanceOf[I]
}

但这不编译,classOf[T]给出错误

  

需要类类型,但发现T

我尝试用classOf[T]替换classManifest[CM].erasure但这不起作用,因为CM需要与ClassManifest进行上下文绑定,显然不可能使用带有隐式参数的有界类型参数

有可能在这里做我想做的事吗?

2 个答案:

答案 0 :(得分:1)

您只需致电template即可获得template.getClass课程。它要求template成为AnyRef的子类型,因此要么将其强制转换为AnyRef(强制原始类型的装箱),要么为T添加上限:< / p>

def instantiateItem[I, T <: AnyRef](implicit mf: Manifest[I], template: T): I = {
  val constructor = mf.erasure.getConstructor(template.getClass)
  constructor.newInstance(template).asInstanceOf[I]
}

如果要明确传递template,如问题中的代码所示,则需要分离隐式和显式参数,例如

def instantiateItem[I, T <: AnyRef](template: T)(implicit mf: Manifest[I]): I = {
  val constructor = mf.erasure.getConstructor(template.getClass)
  constructor.newInstance(template).asInstanceOf[I]
}

又名

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]
}

一般情况下,如果可能,您可以通过精心设计避免使用反射:

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]

答案 1 :(得分:1)

对代码的这些更正应该可以解决问题:

def instantiateItem[I : Manifest, T <: AnyRef : Manifest](template: T): I = {
    val constructor = manifest[I].erasure.getConstructor(manifest[T].erasure)
    constructor.newInstance(template).asInstanceOf[I]
}

I : Manifest语法是隐式参数的首选糖化版本。

请注意,因为Scala 2.10 Manifest将被弃用,而不是TypeTag