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