I wrote this into a REPL:
case class Thingy(s: String)
val things = List(Thingy("x"), Thingy("y"))
things.indexOf("x")
That returns -1. But I would have expected it not to compile, because "x" is a String, not a Thingy. In fact, it turns out it doesn't matter what type you put into indexOf:
things.indexOf(42)
things.indexOf(java.time.LocalDate.now())
These all return -1.
indexOf has this signature:
def indexOf[B >: A](elem: B): Int
I thought >: means B should be a supertype of A, but none of these classes are a supertype of my case class Thingy.
What am I missing here?
B is inferred as java.io.Serializable, which is a supertype of both String and Thingy.1
This is an unfortunate consequence of two things:
Any, Object, Serializable, et al).List being covariant.2
To define indexOf as
def indexOf(elem: A): Int
would put A in a contravariant position, which isn't allowed3 because it would violate the Liskov substitution principle: A List[Thingy] is a List[Any], and you can call .indexOf("x") on a List[Any], therefore you should be able to call .indexOf("x") on a List[Thingy].
1 If they didn't both happen to implement Serializable, it would still infer Any.
2 This is a reason to prefer invariant collections, such as scalaz.IList.
3 Try it - it won't compile: trait Foo[+A] { def bar(a: A) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With