0

我有一个Items 的类层次结构,每个都需要一个相应的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,并且显然不可能将有界类型参数与隐式参数一起使用。

可以在这里做我想做的事吗?

4

2 回答 2

1

template您只需调用即可获得课程template.getClass。它需要template是 的子类型AnyRef,因此要么将其强制转换为AnyRef(强制原始类型装箱),要么为 添加上限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]
}

如果您想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]
于 2012-08-11T15:37:30.880 回答
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.10Manifest将被弃用,取而代之的是TypeTag

于 2012-08-11T15:41:30.640 回答