简体   繁体   English

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

[英]How to instantiate an instance of type represented by type parameter in Scala

example: 例:

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  
.  
.  
.  

Note the second error shows that it knows the actor items are "T"s, but not that the "T" is a subclass of actor, as constrained in the class generic definition. 注意第二个错误表明它知道actor项是“T”,但不是“T”是actor的子类,而是在类通用定义中受到约束。

How can this code be corrected to work (using Scala 2.8)? 如何纠正此代码(使用Scala 2.8)?

EDIT - apologies, I only just noticed your first error. 编辑 - 道歉,我只是注意到你的第一个错误。 There is no way of instantiating a T at runtime because the type information is lost when your program is compiled (via type erasure ) 没有办法在运行时实例化T ,因为编译程序时类型信息会丢失(通过类型擦除

You will have to pass in some factory to achieve the construction: 您将不得不通过一些工厂来实现建设:

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()
  }
} 

This might be used with some actor CalcActor as follows: 这可能与某些actor CalcActor一起使用,如下所示:

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

As an aside: you can use until instead of to : 顺便说一句:你可以使用until ,而不是to

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

Use Manifest: 使用清单:

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 is undocumented in 2.7.X so use it with care. 显然,Manifest在2.7.X中没有记录,所以要小心使用它。 The same code works in 2.8.0 nightly as well. 相同的代码也在每晚2.8.0中运行。

There's now a proper and safer way of doing this. 现在有一个正确而安全的方法。 Scala 2.10 introduced TypeTags, which actually enable us to overcome the problem of erasure when using generic types. Scala 2.10引入了TypeTags,它实际上使我们能够在使用泛型类型时克服擦除问题。

It is now possible to parameterise your class as follows: 现在可以按如下方式对您的类进行参数化:

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

By doing this, we are requiring an implicit ClassTag[T] to be available when the class is instantiated. 通过这样做,我们要求在实例化类时可以使用隐式ClassTag [T]。 The compiler will ensure this is the case and will generate code which passes the ClassTag[T] into the class constructor. 编译器将确保这种情况并生成将ClassTag [T]传递给类构造函数的代码。 The ClassTag[T] will contain all type information about T, and as a result of this the same information that is available to the compiler at compile time (pre-erasure) will now also be available at run-time, enabling us to construct an Array[T]. ClassTag [T]将包含有关T的所有类型信息,因此,编译时编译时可用的相同信息(预擦除)现在也可以在运行时使用,使我们能够构建一个数组[T]。

Note that it is still not possible to do: 请注意,仍然无法做到:

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

The reason this doesn't work is that the compiler has no way of knowing whether class T has a no-arg constructor. 这不起作用的原因是编译器无法知道类T是否具有无参数构造函数。

You can't, as mentioned already, instantiate T because of erasure. 如上所述,由于擦除,您不能实例化T At run-time, there is no T . 在运行时,没有T This is not like C++'s templates, where the substitution happens are compile-time, and multiple classes are actually compiled, for each variation in actual use. 这与C ++的模板不同,其中替换发生在编译时,实际使用的每个变体实际编译了多个类。

The manifest solution is interesting, but assumes there is a constructor for T that does not require parameters. 清单解决方案很有趣,但假设T的构造函数不需要参数。 You can't assume that. 你不能假设。

As for the second problem, the method mailboxSize is protected, so you can't call it on another object. 至于第二个问题,方法mailboxSize是受保护的,因此你不能在另一个对象上调用它。 Update: this is true only of Scala 2.8. 更新:这仅适用于Scala 2.8。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM