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