2009-08-20 9 views
30

ejemplo:Cómo crear una instancia del tipo representado por el parámetro de tipo de 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 
. 
. 
. 

Nota el segundo error demuestra que conoce los elementos de actores son s "T", pero no que la "T" es una subclase de actor, como está restringida en la definición genérica de clase.

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

+0

... se me olvidó mencionar, estoy usando el plugin Eclipse Scala (2.8 cada noche) para esto ... –

+0

Todavía aparece el error en "método mailboxSize no se puede acceder en T", a pesar de usar el fac () funtion pass-in como sugirió. Estoy sorprendido por este resultado, ya que el compilador sabe que T is <: Actor, y que Actor tiene .mailboxSize (se accede dentro de la misma clase BalanceActor, como se muestra). Me pregunto si esto es un error en la versión en particular de 2.8 cada noche que estoy usando ??? ¿No debería compilarse el acceso a .mailboxSize, como usted mismo indicó? ¿Tienes algo similar para trabajar, tal vez en el complemento 2.7.5.final de Eclipse, o compilación de scalac independiente? –

+0

Gracias tanto a oxbow_lakes como a Walter Chang por proporcionar soluciones diferentes, pero ambas viables, para el problema de creación de instancias. –

Respuesta

20

EDIT - disculpas, acabo de notar su primer error. No hay manera de crear instancias de un T en tiempo de ejecución debido a que la información de tipo se pierde cuando se compila el programa (a través de tipo borrado)

Tendrá lugar en 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 podría utilizarse con algún actor CalcActor de la siguiente manera:

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

como acotación al margen: se puede usar en lugar de untilto:

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

Probé su sugerencia con respecto a la especificación de tipo, pero los errores no cambiaron como resultado –

+0

Lo siento, cambié mi respuesta, solo vi el segundo error y no noté la nueva línea –

+0

Gracias por sus sugerencias. ¿Cómo ayudaría la presencia de la fábrica con el error al llamar a un método específico para las subclases de Actor (como el tamaño del buzón mostrado en el ejemplo)? Gracias por el recordatorio sobre el "hasta", pero es difícil romper décadas de hábito de C y Java ... ;-) –

14

Uso 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 = [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 

Por cierto, Manifiesto es indocumentado en 2.7.x a fin de utilizarlo con cuidado. El mismo código funciona en 2.8.0 cada noche también.

+0

Y no funciona si tus A necesitan los parámetros del constructor –

+0

Tu manera funciona, al igual que oxbow_lakes ', para resolver el problema de crear nuevas instancias de A (T es mi ejemplo original) Pero ... el error "method mailboxSize no se puede acceder en A "permanece. ¿Alguna idea de por qué? –

+0

@Paul Me desconcierta también. "self.mailboxSize" se ejecuta satisfactoriamente en 2.7.5 REPL pero provoca el error "method mailboxSize no se puede acceder en scala.actors.Actor" en 2.8.0. "def workerMailboxSizes" compila bien en 2.7.5 también. –

2

No se puede, como se mencionó anteriormente, instanciar T debido a 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 de manifiesto 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 invocarlo en otro objeto. Actualización: esto es cierto solo para Scala 2.8.

11

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

Ahora es posible parametrizar la 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 una ClassTag implícita [T] para estar disponible 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 todavía no es posible hacer:

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

La razón esto no funciona es que el compilador no tiene forma de saber si la clase T tiene un constructor sin argumentos.

+0

¿Cuál es la ventaja de este sobre la respuesta aceptada? Su ejemplo de código no lo deja claro. – 2rs2ts

+0

Lo siento, mi ejemplo no fue excelente, lo he mejorado ahora. La respuesta aceptada no se compila. 'new Array [T] (workers)' no funciona porque el tipo T no se conoce en tiempo de ejecución. La segunda respuesta con Manifest fue mejor, pero TypeTags ha reemplazado a Manifest. – Josh

+0

Por cierto, la razón por la que esta solución es mejor que Manifest es que el uso de Manifest no es seguro: se supone que hay un constructor no-arg para T, que puede no ser el caso. – Josh

Cuestiones relacionadas