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

Posted

技术标签:

【中文标题】如何在Scala中实例化由类型参数表示的类型实例【英文标题】:How to instantiate an instance of type represented by type parameter in Scala 【发布时间】:2009-08-20 11:34:58 【问题描述】:

示例:

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

请注意,第二个错误表明它知道参与者项是“T”,但不知道“T”是参与者的子类,如类通用定义中所限制的那样。

如何更正此代码以使其正常工作(使用 Scala 2.8)?

【问题讨论】:

...忘了提一下,我为此使用了 Eclipse Scala 插件(每晚 2.8)... 尽管按照您的建议使用了传入的 fac() 函数,但仍然收到“无法在 T 中访问方法邮箱大小”的错误。我对这个结果感到惊讶,因为编译器知道 T 是 <: actor .mailboxsize balanceactor eclipse scalac> 感谢 oxbow_lakes 和 Walter Chang 为实例化问题提供了不同但可行的解决方案。 【参考方案1】:

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

你必须通过一些工厂才能完成建造:

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)

【讨论】:

尝试了您关于类型规范的建议,但错误并没有因此而改变 抱歉 - 改变了我的答案 - 我只看到了第二个错误,没有注意到 new T 感谢您的建议。工厂的存在如何帮助解决调用特定于 Actor 子类的方法的错误(例如示例中显示的 mailboxSize)?感谢您对“直到”的提醒,但很难打破 C 和 Java 几十年来的习惯...... ;-) 第二个错误是错误的 - 修复第一个问题,它就会消失。我再次编辑了我的答案 当我说 "erroneous" 时,我的意思是存在错误,因为您的 BalanceActor 类实际上并未使用其泛型类型信息正确编译。【参考方案2】:

使用清单:

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

顺便说一句,Manifest 在 2.7.X 中没有记录,因此请谨慎使用。同样的代码也适用于 2.8.0 nightly。

【讨论】:

你的方法和 oxbow_lakes' 一样有效,用于解决创建 A 的新实例的问题(T 是我的原始示例)但是......错误“方法mailboxSize 无法在A 中访问”遗迹。知道为什么吗? @Paul 这也让我感到困惑。 “self.mailboxSize”在 2.7.5 REPL 中成功执行,但在 2.8.0 中引发“无法在 scala.actors.Actor 中访问方法 mailboxSize”错误。 "def workerMailboxSizes" 在 2.7.5 中也可以正常编译。 @Paul 在查看 Actor 主干上的当前源代码后,我认为他们已经从 trait Actor 中删除了“mailboxSize”。您将需要在对象 Actor 中使用“mailboxSize”。但这可能不是您想要的,因为它只返回“self”的邮箱大小。 @Walter Chang:感谢您检查...。我只查看了 2.8 的 javadocs(在 scaladocs.jcraft.com/2.8.0 上),它没有显示 def 邮箱大小已删除,所以我希望它仍然像 2.7.x 一样存在 由于 m.erasure 现在已弃用,您应该使用 m.runtimeClass.newInstance().asInstanceOf[D].get.toString【参考方案3】:

现在有一种适当且更安全的方法来执行此操作。 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 是否具有无参数构造函数。

【讨论】:

与接受的答案相比,这有什么优势?您的代码示例并不清楚。 对不起,我的例子不是很好——我现在已经改进了。接受的答案实际上没有编译。 new Array[T](workers) 不起作用,因为类型 T 在运行时未知。 Manifest 的第二个答案更好,但 TypeTags 已取代 Manifest。 顺便说一句,这个解决方案比 Manifest 更好的原因是使用 Manifest 是不安全的 - 它假设 T 有一个无参数构造函数,但情况可能并非如此。 @scaling_out 这可能是公认的答案 - 自 09 年以来事情一直在发展,我认为这是 16 年提出问题的人们的最佳发现。 如果类采用构造函数参数,你将如何修改它?【参考方案4】:

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

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

至于第二个问题,方法mailboxSize是受保护的,所以你不能在另一个对象上调用它。 更新:这仅适用于 Scala 2.8。

【讨论】:

以上是关于如何在Scala中实例化由类型参数表示的类型实例的主要内容,如果未能解决你的问题,请参考以下文章

如何绕过 Scala 上的类型擦除?或者,为啥我不能获取我的集合的类型参数?

在类型参数上创建Show实例?

Scala的下一步

在类型层次结构中,从参数到返回类型的 Scala 类型多态性

scala偏函数小栗子

Scala中的构造器与对象文末加群学习哦