2012-04-12 20 views
6

En Scala 2.9.1Genérico recoger por tipo de Scala

Con

def collectFirstOfT[T](la: List[_])(implicit m:Manifest[T]) : Option[T] = { 
    la.collect{case x if m.erasure.isAssignableFrom(x.getClass) => x}. 
    headOption.asInstanceOf[Option[T]]} 

class A 
class B 

por qué esta expresión:

val oB:Option[B] = collectFirstOf(List(new A,new B)) 

compila pero recoge algunos (A), pero

val oB =collectFirstOf[B](List(new A,new B)) 

funciona bien.

¿Cómo inferir T de la opción [T]?

Respuesta

3

Tienes que mirar en la siguiente línea como dos partes separadas, el lado izquierdo de la = y el derecho:

val oB: Option[B] = collectFirstOf(List(new A,new B)) 

Lo que están esperando aquí es que el tipo de la expresión collectFirstOf (el valor r) debe inferirse del tipo del valor de OB. El compilador no puede hacer esto. Tienes que decir específicamente qué tipo estás esperando. Tome el siguiente ejemplo:

val v: Long = 1 + 4 

El tipo de la expresión 1 + 4 es un Int. Este int se convierte en Long. El compilador no puede deducir que desea que el 1 o el 4 sean largos:

Por lo tanto, para solucionar su problema, debe indicar al compilador qué tipo está esperando, de lo contrario, asume java.lang.Object:

val oB = collectFirstOf[B](List(new A,new B)) 

Así que el manifiesto se asigna correctamente, y todo está bien con el mundo. Entonces, ¿por qué el siguiente incluso compilar:

val oB:Option[B] = collectFirstOfT(List(new A,new B)) 
oB: Option[B] = Some([email protected]) 

a primera vista, no parece que esto debería funcionar, pero lo hace. Esto se debe a la collectFirstOfT devuelve realmente una opción [Nada], que se puede convertir de manera segura a una Opción [B]:

scala> val f = collectFirstOfT(List(new A,new B)) 
f: Option[Nothing] = Some([email protected]) 

scala> f.asInstanceOf[Option[B]] 
res4: Option[B] = Some([email protected]) 
+0

¡Buena captura! ¿Cómo puedo evitar que la función sea mal utilizada? (Idealmente, no debería compilarse) – jwinandy

+0

Una manera rápida y fácil sería agregar un argumento explícito, la clase: collectFirstOfT [T] (cls: Class [T], la: List [_]), luego llamar como: collectFirstOfT (classOf [B], List (nuevo A, nuevo B)). Esto devolvería una Opción [B] como se esperaba. –

0

Porque el compilador no puede inferir T de agrupamientos, por lo que debe escribirlo explícitamente. En el primer caso, collect acepta toda la lista.

+0

'T' (o' Opción [T] ') es solo para el tipo de devolución. '{case x if m.erasure.isAssignableFrom (x.getClass) => x}' es un 'PartialFunction [-Any, + Any]'. – jwinandy

3

Este:

val oB:Option[B] = collectFirstOfT(List(new A,new B)) 

equivale a esto:

val oB: Opción [B] = collectFirstOfT [Nothing] (Lista (nuevo A, nuevo B))

Dado que Nothing es una subclase de todo, se puede asignar desde A. Por desgracia, también se puede asignar desde B, lo que significa que puede asignar un Option[Nothing] a un Option[B].

Dato curioso: eso es cierto porque Option es covariante. Si no fuera así, entonces T tendría que inferirse como B, lo que lo haría funcionar.

Dato curioso 2: este código no se compila en el trunk de ayer.

Cuestiones relacionadas