Reputation: 3257
I wanted to make an algorithm that filters elements by type
I did something like that
trait Valor
case class A() extends Valor
case class B() extends Valor
case class C() extends Valor
val list : List[Valor] = List(
A(), A(),
B(), B(), B(),
C()
)
def getOfType[T](list: List[_]) = {
list.filter {
case _ : T => true
case _ => false
}
}
getOfType[A](list)
But the output of it is
List[Any] = List(A(), A(), B(), B(), B(), C())
I'd understand if it wouldn't compile at all. But why does it compile and give such strange result?
Upvotes: 0
Views: 181
Reputation: 2169
(If you are interested in possible solution; does not fit in comment)
First, we need some imports
import scala.reflect.ClassTag
Then we can tell the compiler to generate a ClassTag
for the type parameter T
at compile-time.
Using this ClassTag
we can compare the desired class with the class of each element.
def getOfType[T](list: List[_])(implicit tag: ClassTag[T]): List[_] = {
list.filter { elem => tag.runtimeClass.equals(elem.getClass) }
}
Now, we can go even further and return a List of T
s instead of a List of Any
def getOfType[T](list: List[_])(implicit tag: ClassTag[T]): List[T] = {
list.collect {
case elem if tag.runtimeClass.equals(elem.getClass) => elem.asInstanceOf[T]
}
}
Interestingly, pattern matching on the type magically works after introducing the implicit ClassTag
evidence, which lets us simplify things a lot
def getOfType[T : ClassTag](list: List[_]): List[T] = {
list.collect {
case elem : T => elem
}
}
This works for these simple classes A
, B
& C
. However, if you want to filter out polymorphic classes like List[String]
, you will need to use TypeTag
s. You can have a look at this example gist which demonstrates polymorphic type filtering.
We could probably add an additional, explicit Class[T]
argument to the function instead, which we could use to do the comparison with.
Upvotes: 4
Reputation: 52701
It's because the : T
is eliminated by erasure (there's a warning) since the generic aspect is lost after compilation, meaning that there's no way for it to check the generic type at runtime. So basically it's not doing a check at all. The parameter is a list of anything (List[_]
), and the filter doesn't filter at all, so it's producing a list of anything.
Upvotes: 3