2011-03-30 9 views
12

me gustaría tener un método que devuelve una clase de un cierto tipo, pero quiero que el método se comporte de manera diferente dependiendo de si o no la clase amplía un rasgo particular de la siguiente manera:¿Puedo realizar la coincidencia en un parámetro de tipo en Scala para ver si implementa un rasgo?

 

case class ClassA extends TraitA 
case class ClassB extends TraitB 
case class ClassC extends TraitA 
... 
def myfunc[T]():T = { 
    T match { 
    case TraitA => // return new T in a particular way 
    case TraitB => // ditto 
    } 
} 
 

Es esto posible ¿O lo estoy haciendo de la manera incorrecta?

Gracias

Respuesta

13

No puede comparar tipos directamente, porque no hay nada allí para comparar (en tiempo de ejecución, debido a erasure). Se podría trabajar en una representación de su clase:

trait TraitA { } 
trait TraitB { } 
class ClassA extends TraitA { } 
class ClassB extends TraitB { } 

def myFunc[T](clazz: Class[T]) = { 
    if (classOf[TraitA] isAssignableFrom clazz) println("A") 
    else if (classOf[TraitB] isAssignableFrom clazz) println("B") 
    else println("?") 
} 

scala> myFunc(classOf[ClassA]) 
A 

scala> myFunc(classOf[String]) 
? 

o que pueda coincidir con el patrón en instancias de la clase:

def myFunc2[T](t: T) = t match { 
    case _: TraitA => println("A") 
    case _: TraitB => println("B") 
    case _ => println("?") 
} 

scala> myFunc2(new ClassA) 
A 

scala> myFunc2(Some(5)) 
? 

También puede utilizar el primer enfoque de una manera sintácticamente menos molesto a través de clases manifiesta:

def myFunc3[T](implicit mf: ClassManifest[T]) = { 
    val clazz = mf.erasure 
    if (classOf[TraitA] isAssignableFrom clazz) println("A") 
    else if (classOf[TraitB] isAssignableFrom clazz) println("B") 
    else println("?") 
} 

scala> myFunc3[ClassA] 
A 

scala> myFunc3[String] 
? 

y se puede elegir diferentes tipos de expedición también si el if/else se convierte en manejable:

object MyFunc { 
    val dispatch = Map(
    classOf[TraitA] -> (() => println("A")), 
    classOf[TraitB] -> (() => println("B")) 
) 
    val default =() => println("?") 
    def apply[T](implicit mf: ClassManifest[T]) = 
    dispatch.find(_._1 isAssignableFrom mf.erasure).map(_._2).getOrElse(default)() 
} 

scala> MyFunc[ClassA] 
A 

scala> MyFunc[String] 
? 

Tenga en cuenta que cualquier código genérico que utilice esto tendrá que tener un manifiesto de clase disponible (ya sea como un parámetro implícito o en forma abreviada, [T: ClassManifest].

+0

De hecho encontré 2 maneras que funcionan bien en mi caso: 1) classOf [TraitA] isAssignableFrom clazz como sugeriste 2) if (mf <: codefly

+0

+1: al principio me molestó tanto con su respuesta como con Scala ("No se pueden comparar tipos directamente, porque no hay ** nada * * hay para comparar "... Hay un ** tipo **, y un tipo es ciertamente algo. Deberías haber escrito" no hay ningún * valor * "en mi humilde opinión. Pero luego explicaste sobre los manifiestos de clase, y eso dio en el clavo. ¡Así que gracias! – rsenna

+0

@rsenna - Con "no hay nada" quise decir que no hay nada allí _en tiempo de ejecución_ porque 'T' solo le dice al compilador cómo debe mantener sus tipos en línea recta en tiempo de compilación. Entonces en realidad ... un tipo genérico es, en tiempo de ejecución, nada. Se fue. ("Borrado de tipo.") Los manifiestos proporcionan una forma de suministrar esa información en tiempo de compilación en tiempo de ejecución. ('ClassTag' o' TypeTag' es la nueva forma de hacerlo en 2.10, aunque la forma antigua todavía funciona.) De todos modos, me alegro de que la haya encontrado útil incluso si mi fraseología no fue muy discreta. –

1

Necesita una instancia para verificar el tipo. Un genérico en sí mismo es solo un marcador de posición para un tipo. Se podría hacer algo como esto:

trait bTrait //common base trait 
trait TraitA extends bTrait 
trait TraitB extends bTrait 

class ClassA extends TraitA 
class ClassB extends TraitB 

def myFunc[T <: bTrait](t:T) : String = //passing explicitly an instance of type T 
{ 
    t match { 
    case _ : TraitA => "TraitA" 
    case _ : TraitB => "TraitB" 
    } 
} 

println(myFunc(new ClassA)) //prints TraitA 
+0

De acuerdo, si tuviera una instancia, ¡esto sería fácil! – codefly

2

"quiere que el método de comportarse de forma diferente en función de si o no la clase amplía un rasgo particular"

Esto es casi una descripción canónica de la herencia. ¿No puedes tener un método en cada rasgo que encapsule el comportamiento diferente que buscas?

+0

El problema principal aquí es que no tengo una instancia del objeto. Basado en el tipo del objeto, necesito determinar dónde ir y encontrarlo (en qué base de datos en este caso) – codefly

Cuestiones relacionadas