Search code examples
scalatype-erasure

In Scala how do I filter by reified types at runtime?


I have a Scala collection that contains objects of different subtypes.

abstract class Base

class A extends Base

class B extends Base

val a1 = new A()
val a2 = new A()
val b = new B()
val s = List(a1, a2, b)

I'd like to filter out all the A objects or the B objects. I can do this easily if I know the object I want to filter on at compile time.

s.filter(_.isInstanceOf[A]) // Give me all the As
s.filter(_.isInstanceOf[B]) // Give me all the Bs

Can I do it if I only know the object type to filter on at runtime? I want to write a function like this.

def filterType(xs:List[Base], t) = xs.filter(_.isInstanceOf[t])

Where t indicates whether I want objects of type A or B.

Of course I can't actually write it this way because of type erasure. Is there an idiomatic Scala way to work around this using type tags? I've been reading the Scala type tag documentation and relevant StackOverflow posts, but I can't figure it out.


Solution

  • This has come up a few times. Duplicate, anyone?

    scala> trait Base
    defined trait Base
    
    scala> case class A(i: Int) extends Base 
    defined class A
    
    scala> case class B(i: Int) extends Base 
    defined class B
    
    scala> val vs = List(A(1), B(2), A(3))
    vs: List[Product with Serializable with Base] = List(A(1), B(2), A(3))
    
    scala> def f[T: reflect.ClassTag](vs: List[Base]) = vs collect { case x: T => x }
    f: [T](vs: List[Base])(implicit evidence$1: scala.reflect.ClassTag[T])List[T]
    
    scala> f[A](vs)
    res0: List[A] = List(A(1), A(3))