Подтвердить что ты не робот

Как создать экземпляр типа, представленного параметром типа в 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  
.  
.  
.  

Обратите внимание, что вторая ошибка показывает, что он знает, что элементы-участники являются "Т", но не "Т" - это подкласс актора, как ограниченный в определении общего класса.

Как можно исправить этот код для работы (используя Scala 2.8)?

4b9b3361

Ответ 1

EDIT - извинения, я только что заметил вашу первую ошибку. Не существует способа создания экземпляра T во время выполнения, поскольку информация о типе теряется при компиляции вашей программы (с помощью типа erasure)

Вам нужно будет пройти через factory для достижения конструкции:

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)

Ответ 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 = [email protected]" in console
val foo = new Foo[Bar](bar1)
val bar2 = foo.create    // prints "bar2: Bar = [email protected]" in console
bar2.isInstanceOf[Bar]   // prints "Boolean = true" in console

Кстати, манифест недокументирован в 2.7.X, поэтому используйте его с осторожностью. Этот же код работает и в 2.8.0 ночей.

Ответ 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 конструктор no-arg.

Ответ 4

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

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

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