web-dev-qa-db-ja.com

Scalaの型パラメーターで表される型のインスタンスをインスタンス化する方法

例:

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

2番目のエラーは、アクターアイテムが「T」であることを認識しているが、クラスの一般的な定義で制約されているように、「T」がアクターのサブクラスであることを示していないことに注意してください。

このコードを修正して機能させるにはどうすればよいですか(Scala 2.8)を使用)?

35
scaling_out

[〜#〜] edit [〜#〜]-お詫びします。最初のエラーに気づいただけです。プログラムのコンパイル時に型情報が失われるため、実行時に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)
25
oxbow_lakes

マニフェストを使用する:

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

ところで、マニフェストは2.7.Xで文書化されていないため、注意して使用してください。同じコードが2.8.0で毎晩機能します。

16
Walter Chang

これを行うための適切で安全な方法があります。 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に関するすべての型情報が含まれます。その結果、コンパイル時にコンパイラーが利用できるのと同じ情報(事前消去)が実行時にも利用できるようになり、構築が可能になります。 Array [T]。

それでも実行できないことに注意してください。

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

これが機能しない理由は、クラスTに引数なしのコンストラクターがあるかどうかをコンパイラーが知る方法がないためです。

13
Josh

すでに述べたように、消去のためにTをインスタンス化することはできません。実行時には、Tはありません。これは、置換がコンパイル時に行われ、実際の使用のバリエーションごとに複数のクラスが実際にコンパイルされるC++のテンプレートとは異なります。

マニフェストソリューションは興味深いものですが、パラメーターを必要としないTのコンストラクターがあることを前提としています。あなたはそれを仮定することはできません。

2番目の問題については、メソッドmailboxSizeが保護されているため、別のオブジェクトで呼び出すことはできません。 更新:これはScala 2.8の場合にのみ当てはまります。

2