2009-08-20 2 views
30

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

+0

... забыли упомянуть, я использую плагин Eclipse Scala (2,8 ночи) для этого ... –

+0

По-прежнему получать сообщение об ошибке «метод mailboxSize не может быть доступен в T», несмотря на использование фрейма () funtion прошел, как вы предложили. Я удивлен этим результатом, поскольку компилятор знает, что T является <: Actor, и что у Актера есть .mailboxSize (доступ к нему в пределах того же класса BalanceActor, как показано) Мне интересно, является ли это ошибкой в ​​конкретной версии 2,8 ночи я использую ??? Должен ли доступ к компиляции .mailboxSize, как вы сами заявили? У вас есть что-то похожее на работу, возможно, на плагин 2.7.5.final Eclipse или автономную компиляцию скаляра? –

+0

Благодаря как oxbow_lakes, так и Walter Chang для предоставления различных, но и работоспособных решений для проблемы создания экземпляров. –

ответ

20

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) 
+0

Пробовал ваше предложение относительно спецификации типа, но ошибки не изменились в результате –

+0

Извините - изменил мой ответ - я только увидел вторую ошибку и не заметил строку 'new T' –

+0

Спасибо за ваши предложения. Как бы присутствие фабрики помогло с ошибкой вызывать метод, специфичный для подклассов Актера (например, размер почтового ящика, показанный в примере)? Спасибо за напоминание о «до», но трудно отменить десятилетия привычки от C и Java ... ;-) –

14

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

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 

BTW, Manifest недокументирован в 2.7.x, так что используйте его с осторожностью. Тот же код работает и в 2.8.0 ночей.

+0

И не работает, если параметры конструктора потребности A –

+0

Ваш путь работает, а также oxbow_lakes ', для решения проблемы создания новых экземпляров A (T - мой исходный пример) Но ... метод ошибки mailboxSize не могут быть доступны в A "остается. Любая идея почему? –

+0

@Paul Это тоже озадачивает меня. «self.mailboxSize» успешно выполняется в 2.7.5 REPL, но вызывает «метод mailboxSize не может быть доступен в файле scala.actors.Actor» в 2.8.0. «def workerMailboxSizes» также отлично компилируется в 2.7.5. –

2

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

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

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

11

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

+0

В чем преимущество этого по принятому ответу? Ваш пример кода не делает это ясным. – 2rs2ts

+0

Извините, мой пример не был отличным - я улучшил его сейчас. Принятый ответ на самом деле не компилируется. 'new Array [T] (workers)' не работает, потому что тип T неизвестен во время выполнения. Второй ответ с Manifest был лучше, но TypeTags заменили Manifest. – Josh

+0

Кстати, причина, по которой это решение лучше, чем Manifest, заключается в том, что использование Manifest небезопасно - оно предполагает, что для T существует конструктор no-arg, что может и не быть. – Josh

Смежные вопросы