如何在 Scala 中实例化由类型参数表示的类型实例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1305563/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to instantiate an instance of type represented by type parameter in Scala
提问by scaling_out
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.
请注意,第二个错误表明它知道演员项目是“T”,但不知道“T”是演员的子类,如类泛型定义中的约束。
How can this code be corrected to work (using Scala 2.8)?
如何更正此代码以使其正常工作(使用 Scala 2.8)?
采纳答案by oxbow_lakes
EDIT- apologies, I only just noticed your first error. There is no way of instantiating a Tat runtime because the type information is lost when your program is compiled (via type erasure)
编辑- 抱歉,我只是注意到你的第一个错误。无法T在运行时实例化 a ,因为编译程序时类型信息会丢失(通过类型擦除)
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 CalcActoras follows:
这可能与某些演员一起使用,CalcActor如下所示:
val ba = new BalanceActor[CalcActor]( { () => new CalcActor } )
ba.start
As an aside: you can use untilinstead of to:
顺便说一句:您可以使用until代替to:
val size = 10
0 until size //is equivalent to:
0 to (size -1)
回答by Walter Chang
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. The same code works in 2.8.0 nightly as well.
顺便说一句,Manifest 在 2.7.X 中没有记录,所以请小心使用它。同样的代码也适用于 2.8.0 nightly。
回答by Josh
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. The compiler will ensure this is the case and will generate code which passes the ClassTag[T] into the class constructor. 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] 在类被实例化时可用。编译器将确保是这种情况,并将生成将 ClassTag[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 是否具有无参数构造函数。
回答by Daniel C. Sobral
You can't, as mentioned already, instantiate Tbecause of erasure. At run-time, there is no 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.
如前所述,您不能T因为擦除而实例化。在运行时,没有T. 这与 C++ 的模板不同,在 C++ 的模板中,替换发生在编译时,对于实际使用中的每个变体,实际上编译了多个类。
The manifest solution is interesting, but assumes there is a constructor for Tthat does not require parameters. You can't assume that.
manifest 解决方案很有趣,但假设有一个T不需要参数的构造函数。你不能假设。
As for the second problem, the method mailboxSizeis protected, so you can't call it on another object. Update:this is true only of Scala 2.8.
至于第二个问题,这个方法mailboxSize是受保护的,所以你不能在另一个对象上调用它。更新:这仅适用于 Scala 2.8。

