Как создать экземпляр типа, представленного параметром типа в Scala - PullRequest
33 голосов
/ 20 августа 2009

пример:

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)?

Ответы [ 4 ]

24 голосов
/ 20 августа 2009

РЕДАКТИРОВАТЬ - извините, я только что заметил вашу первую ошибку. Невозможно создать экземпляр 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)
15 голосов
/ 20 августа 2009

Использовать манифест:

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 ночью.

13 голосов
/ 05 марта 2015

Теперь есть правильный и безопасный способ сделать это. В 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 конструктор без аргументов.

2 голосов
/ 20 августа 2009

Вы не можете, как уже упоминалось, создать экземпляр T из-за стирания. Во время выполнения нет T. Это не похоже на шаблоны C ++, где подстановка происходит во время компиляции, и несколько классов фактически компилируются для каждого варианта в реальном использовании.

Манифестное решение интересно, но предполагает, что для T есть конструктор, который не требует параметров. Вы не можете предполагать это.

Что касается второй проблемы, метод mailboxSize защищен, поэтому вы не можете вызвать его для другого объекта. Обновление: Это верно только для Scala 2.8.

...