如何在Scala中实例化由type参数表示的类型的实例

时间:2009-08-20 11:34:58

标签: scala generics type-parameter

示例:

import scala.actors._  
import Actor._  

class BalanceActor[T <: Actor] extends Actor {  
  val workers: Int = 10  

  private lazy val actors = new Array[T](workers)  

  override def start() = {  
    for (i <- 0 to (workers - 1)) {  
      // error below: classtype required but T found  
      actors(i) = new T  
      actors(i).start  
    }  
    super.start()  
  }  
  // error below:  method mailboxSize cannot be accessed in T
  def workerMailboxSizes: List[Int] = (actors map (_.mailboxSize)).toList  
.  
.  
.  

注意第二个错误表明它知道actor项是“T”,但并不是说“T”是actor的子类,而是在类通用定义中受约束。

如何更正此代码(使用Scala 2.8)?

4 个答案:

答案 0 :(得分:24)

编辑 - 道歉,我只是注意到你的第一个错误。无法在运行时实例化T,因为编译程序时类型信息会丢失(通过类型删除

你必须通过一些工厂来实现建设:

class BalanceActor[T <: Actor](val fac: () => T) extends Actor {
  val workers: Int = 10

  private lazy val actors = new Array[T](workers)

  override def start() = {
    for (i <- 0 to (workers - 1)) {
      actors(i) = fac() //use the factory method to instantiate a T
      actors(i).start
    }
    super.start()
  }
} 

这可能与某个演员CalcActor一起使用,如下所示:

val ba = new BalanceActor[CalcActor]( { () => new CalcActor } )
ba.start

暂且不说:您可以使用until代替to

val size = 10
0 until size //is equivalent to:
0 to (size -1)

答案 1 :(得分:15)

使用清单:

class Foo[A](a: A)(implicit m: scala.reflect.Manifest[A]) {
  def create: A = m.erasure.newInstance.asInstanceOf[A]
}

class Bar

var bar1 = new Bar       // prints "bar1: Bar = Bar@321ea24" in console
val foo = new Foo[Bar](bar1)
val bar2 = foo.create    // prints "bar2: Bar = Bar@6ef7cbcc" in console
bar2.isInstanceOf[Bar]   // prints "Boolean = true" in console
BTW,Manifest在2.7.X中没有记录,所以要小心使用它。同样的代码也可以在每晚2.8.0中使用。

答案 2 :(得分:13)

现在这是一种正确而安全的方式。 Scala 2.10引入了TypeTags,它实际上使我们能够在使用泛型类型时克服擦除问题。

现在可以按如下方式对您的课程进行参数设置:

class BalanceActor[T <: Actor :ClassTag](fac: () => T) extends Actor {
    val actors = Array.fill[T](10)(fac())
}

通过这样做,我们要求在实例化类时可以使用隐式ClassTag [T]。编译器将确保这种情况并生成将ClassTag [T]传递给类构造函数的代码。 ClassTag [T]将包含有关T的所有类型信息,因此,编译时编译时可用的相同信息(预擦除)现在也可以在运行时使用,使我们能够构建一个数组[T]。

请注意,仍然无法做到:

class BalanceActor[T <: Actor :ClassTag] extends Actor {
    val actors = Array.fill[T](10)(new T())
}

这不起作用的原因是编译器无法知道类T是否具有无参数构造函数。

答案 3 :(得分:2)

如上所述,由于擦除,您无法实例化T。在运行时,没有T。这不像C ++的模板,其中替换发生在编译时,并且实际编译多个类,用于实际使用中的每个变体。

清单解决方案很有意思,但假设T有一个不需要参数的构造函数。你不能假设。

至于第二个问题,方法mailboxSize受到保护,因此您无法在另一个对象上调用它。 更新:这仅适用于Scala 2.8。