type parameter generic scala generics type-parameter

parameter - Cómo crear una instancia de una instancia del tipo representado por el parámetro de tipo en Scala



scala default parameters (4)

ejemplo:

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

Tenga en cuenta que el segundo error muestra que sabe que los elementos del actor son "T" s, pero no que la "T" es una subclase del actor, como está restringido en la definición genérica de la clase.

¿Cómo se puede corregir este código para que funcione (utilizando Scala 2.8)?


Ahora hay una forma correcta y más segura de hacer esto. Scala 2.10 introdujo TypeTags, que en realidad nos permiten superar el problema del borrado al usar tipos genéricos.

Ahora es posible parametrizar su clase de la siguiente manera:

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

Al hacer esto, estamos requiriendo que esté disponible una ClassTag [T] implícita cuando se crea una instancia de la clase. El compilador se asegurará de que este sea el caso y generará un código que pase el ClassTag [T] en el constructor de la clase. ClassTag [T] contendrá toda la información de tipo sobre T, y como resultado de esto, la misma información que está disponible para el compilador en tiempo de compilación (borrado previo) ahora también estará disponible en tiempo de ejecución, lo que nos permite construir una Matriz [T].

Tenga en cuenta que aún no es posible hacerlo:

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

La razón por la que esto no funciona es porque el compilador no tiene forma de saber si la clase T tiene un constructor no-arg.


No se puede, como ya se mencionó, crear una instancia de T por borrado. En tiempo de ejecución, no hay T Esto no es como las plantillas de C ++, donde la sustitución ocurre en tiempo de compilación, y se compilan múltiples clases, para cada variación en el uso real.

La solución manifiesta es interesante, pero supone que hay un constructor para T que no requiere parámetros. No puedes asumir eso.

En cuanto al segundo problema, el método mailboxSize está protegido, por lo que no puede llamarlo a otro objeto. Actualización: esto es cierto solo de Scala 2.8.


Use Manifiesto:

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

Por cierto, Manifiesto no está documentado en 2.7.X así que úselo con cuidado. El mismo código funciona en 2.8.0 cada noche también.


EDITAR - disculpas, acabo de notar tu primer error. No hay forma de instanciar una T en tiempo de ejecución porque la información de tipo se pierde cuando se compila el programa (mediante borrado de tipo)

Tendrás que pasar por alguna fábrica para lograr la construcción:

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() } }

Esto se puede usar con algún actor CalcActor siguiente manera:

val ba = new BalanceActor[CalcActor]( { () => new CalcActor } ) ba.start

Como un aparte: puedes usar until lugar de:

val size = 10 0 until size //is equivalent to: 0 to (size -1)