2011-03-30 13 views
12

Vorrei avere un metodo che restituisce una classe di un certo tipo, ma voglio il metodo di comportarsi in modo diverso a seconda se la classe estende un determinato tratto come segue:Posso eseguire la corrispondenza su un parametro di tipo in Scala per vedere se implementa un tratto?

 

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

Questo è possibile o sto andando su di esso nel modo sbagliato?

Grazie

risposta

13

Non è possibile confrontare i tipi direttamente, perché non c'è nulla da confrontare (in fase di esecuzione, a causa di erasure). Si potrebbe lavorare su una rappresentazione della classe:

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]) 
? 

oppure si può abbinare modello sul istanze della classe:

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)) 
? 

È anche possibile utilizzare il primo approccio in modo sintatticamente meno invadente via classe manifesta:

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] 
? 

ed è possibile scegliere diversi tipi di spedizione anche se il if/else diventa wieldy:

Si noti che qualsiasi codice generico che si utilizza da questo dovrà avere un manifest di classe disponibile (sia come parametro implicito o in stenografia, [T: ClassManifest].

+0

Ho effettivamente trovato 2 modi che funzionano bene nel mio caso: 1) classOf [TraitA] isAssignableFrom come hai suggerito 2) if (mf <: codefly

+0

+1: all'inizio mi sono irritato sia con la tua risposta sia con Scala ("Non puoi confrontare i tipi direttamente, perché non c'è ** nulla * * lì per confrontare "... C'è un ** tipo **, e un tipo è sicuramente qualcosa. Avresti dovuto scrivere" non c'è alcun * valore * "IMHO). Ma poi hai spiegato dei manifesti di classe, e questo è stato il colpo. Quindi grazie! – rsenna

+0

@rsenna - Con "non c'è nulla" intendevo dire che non c'è niente lì _at runtime_ perché 'T' sta solo dicendo al compilatore come dovrebbe mantenere i tuoi tipi direttamente in fase di compilazione. Quindi in realtà ... un tipo generico è, in fase di esecuzione, nulla. È andato. ("Tipo cancellato.") I manifest forniscono un modo per fornire informazioni in fase di compilazione in fase di runtime. ('ClassTag' o' TypeTag' è il nuovo modo di farlo in 2.10, anche se il vecchio modo funziona ancora.) Comunque, felice che tu l'abbia trovato utile anche se il mio fraseggio non era molto delicato! –

1

È necessaria un'istanza per verificare il tipo. Un generico di per sé è solo un segnaposto per un tipo. Si potrebbe fare qualcosa di simile:

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

D'accordo, se avessi un'istanza sarebbe facile! – codefly

2

"vogliono il metodo di comportarsi in modo diverso a seconda se la classe estende un tratto particolare"

Questo è quasi una descrizione canonica di eredità. Non puoi avere un metodo in ogni tratto che incapsula il diverso comportamento che stai cercando?

+0

Il problema principale qui è che non ho un'istanza dell'oggetto. In base al tipo di oggetto, ho bisogno di determinare dove andare e trovarlo (in quale database in questo caso) – codefly

Problemi correlati