Pergunta

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.

Foi útil?

Solução

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

Outras dicas

Type erasure will destroy any information in type parameters, but objects still know what class they belong to. Because of this, we cannot filter on arbitrary types, but we can filter by class or interface/trait. ClassTag is preferable to TypeTag here.

import scala.reflect.ClassTag

def filterType[T: ClassTag](xs: List[Base]) = xs.collect {
  case x: T => x
}

Which we can use like:

scala> filterType[B](s)
res29: List[B] = List(B@42096939)

scala> filterType[Base](s)
res30: List[Base] = List(A@8dbc09c, A@625f8cc7, B@42096939)

This method is safe at run-time if type T is not generic. If there was a class C[T] extends Base we could not safely filter on C[String].

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top