Let's say I have the following types
class Foo
trait Bar
Is there a way to make a method which takes in a Type parameter, T, and determine if that T is a Bar? For example,
def isBar[T <: Foo: Manifest] =
classOf[Bar].isAssignableFrom(manifest[T].erasure)
Sadly, isBar[Foo with Bar]
is false
because erasure seems to erase mixins.
Also, manifest[Foo with Bar] <:< manifest[Bar]
is false
Is this possible at all?
I looked at this question: How to tell if a Scala reified type extends a certain parent class?
but that answer doesn't work with mixed-in traits as they seem to be erased as evidenced above.
This can be achieved with TypeTags (at least 2.10M7):
scala> class Foo; trait Bar
defined class Foo
defined trait Bar
scala> import reflect.runtime.universe._
import reflect.runtime.universe._
scala> def isBar[A <: Foo : TypeTag] = typeOf[A].baseClasses.contains(typeOf[Bar].typeSymbol)
isBar: [A <: Foo](implicit evidence$1: reflect.runtime.universe.TypeTag[A])Boolean
scala> isBar[Foo]
res43: Boolean = false
scala> isBar[Foo with Bar]
res44: Boolean = true
TypeTags provide a 1:1 translation of Scala types because they represent the types the compiler knows. Therefore they are much more powerful than plain old Manifests:
scala> val fooBar = typeTag[Foo with Bar]
fooBar: reflect.runtime.universe.TypeTag[Foo with Bar] = TypeTag[Foo with Bar]
With the method tpe
we get full access to Scalas new Reflection:
scala> val tpe = fooBar.tpe // equivalent to typeOf[Foo with Bar]
tpe: reflect.runtime.universe.Type = Foo with Bar
scala> val tpe.<tab><tab> // lot of nice methods here
=:= asInstanceOf asSeenFrom baseClasses baseType contains declaration
declarations erasure exists find foreach isInstanceOf kind
map member members narrow normalize substituteSymbols substituteTypes
takesTypeArgs termSymbol toString typeConstructor typeSymbol widen
It is possible to do this pre-2.10, just not (as far as I know) with manifests:
def isBar[T <: Foo](implicit ev: T <:< Bar = null) = ev != null
It's a bit of a hack, but it works as desired.
scala> isBar[Foo with Bar]
res0: Boolean = true
scala> isBar[Foo]
res1: Boolean = false
You can resolve it without reflection by using typeclasses:
trait IsBar[T] {
def apply():Boolean
}
trait LowerLevelImplicits {
implicit def defaultIsBar[T] = new IsBar[T]{
def apply() = false
}
}
object Implicits extends LowerLevelImplicits {
implicit def isBarTrue[T <: Bar] = new IsBar[T] {
def apply() = true
}
}
def isBar[T<:Foo]( t: T )( implicit ib: IsBar[T] ) = ib.apply()
scala> import Implicits._
scala> isBar( new Foo )
res6: Boolean = false
scala> isBar( new Foo with Bar )
res7: Boolean = true
Another typeclass usage(more generic):
trait SubClassGauge[A, B] {
def A_isSubclassOf_B: Boolean
}
implicit class IsSubclassOps[A](a: A) {
def isSubclassOf[B](implicit ev: SubClassGauge[A, B]): Boolean = ev.A_isSubclassOf_B
}
trait LowerLevelImplicits {
implicit def defaultSubClassGauge[A, B] = new SubClassGauge[A, B] {
override def A_isSubclassOf_B: Boolean = false
}
}
object Implicits extends LowerLevelImplicits {
implicit def subClassGauge[A <: B, B]: SubClassGauge[A, B] = new SubClassGauge[A, B] {
override def A_isSubclassOf_B: Boolean = true
}
}
trait Prime
class NotSuper
class Super extends Prime
class Sub extends Super
class NotSub
Now, in REPL:
@ import Implicits._
import Implicits._
@ (new Sub).isSubclassOf[NotSuper]
res29: Boolean = false
@ (new Sub).isSubclassOf[Super]
res30: Boolean = true
@ (new Sub).isSubclassOf[Prime]
res31: Boolean = true
@ (new Super).isSubclassOf[Prime]
res32: Boolean = true
@ (new Super).isSubclassOf[Sub]
res33: Boolean = false
@ (new NotSub).isSubclassOf[Super]
res34: Boolean = false
TypeTag
now belongs to scala reflect package. One needs to add extra dependency to use it.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.