Can I perform matching on a type parameter in Scala to see if it implements a trait?
I would like to have a method which returns a class of a certain type, but I want the method to beha开发者_运维知识库ve differently depending on whether or not the class extends a particular trait as follows:
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
}
}
Is this possible, or am I going about it the wrong way?
Thanks
You can't compare types directly, because there isn't anything there to compare (at runtime, due to erasure). You could work on a representation of your class:
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])
?
or you can pattern match on instances of the class:
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))
?
You can also use the first approach in a syntactically less obtrusive way via class manifests:
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]
?
and you can choose different sorts of dispatch also if the if/else becomes wieldy:
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]
?
Note that any generic code that you use this from will need to have a class manifest available (either as an implicit parameter or in shorthand, [T: ClassManifest]
.
You need an instance to check the type. A generic by itself is just a placeholder for a type. You could do something like this:
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
"want the method to behave differently depending on whether or not the class extends a particular trait"
This is almost a canonical description of inheritance. Can't you have a method in each trait that encapsulates the differing behaviour you're after?
精彩评论