I am sorry to repost it here as I did not get a satisfying answer on Stackoverflow
Consider
trait Fruit {
val name: String
}
case class Apple(override val name: String) extends Fruit
case class Banana(override val name: String) extends Fruit
When I define a function to collect only specific types
def onlyT[T](list: List[Fruit]): List[T] = list.collect { { case x: T => x } }
def onlyT2[T: ClassTag](list: List[Fruit]): List[T] = list.collect { { case x: T => x } }
val fruitList: List[Fruit] = List(Apple("app1"), Apple("app2"), Apple("app3"), Apple("app4"), Banana("ban1"), Banana("ban2"), Banana("ban3"))
And below val a
is of type List[Banana]
but contains entries of type Apple
.
val a: List[Banana] = onlyT[Banana](fruitList)
val b: List[Banana] = fruitList.collect { case x: Banana => x }
val c: List[Banana] = onlyT2[Banana](fruitList)
println(a) // List(Apple(app1), Apple(app2), Apple(app3), Apple(app4), Banana(ban1), Banana(ban2), Banana(ban3))
println(b) // List(Banana(ban1), Banana(ban2), Banana(ban3))
println(c) // List(Banana(ban1), Banana(ban2), Banana(ban3))
Is this expected? Or is it a typesystem bug? I know at runtime type is erased, but not sure why it works for b but not a? I think the compiler should not allow a if it will be like this. Anything I am missing?
FYI, this is not a question to solve the issue I can resolve it by using ClassTag. The question is: Why is scala compiler allowing the type declaration val a: List[Banana]
and we end up with a list of apples and bananas of type List[Banana]
I believe this breaks the type system, it acts as if this is a dynamically typed language.
Is that an expected problem with type erasure? The problem is I don’t even have a compiler warning for the line val a: List[Banana] ...
only warning for onlyT
definition