I struggle to understand Scala's covariance in combination with lower bounds. I will illustrate my confusion in the following code snippet with 2 compilation errors.
class Queue[+T]:
def enqueue[U >: T](x: U): Queue[U] = null
class IntQueue extends Queue[Int]:
override def enqueue[Int](x: Int): Queue[Int] =
println(math.sqrt(x)) // 1) Found: (x : Int) Required: Double
super.enqueue(x) // 2) Found: Queue[Any] Required: Queue[Int]
First, there is a generic class Queue
which takes one type parameter with covariance annotation +
. This is OK as I want to make assignments such as val a: Queue[Any] = IntQueue()
. Its enqueue
method has a lower bound for its type parameter, U >: T
. This is needed because the x
would otherwise be in a contravariant position, allowing for nasty things, similar to ArrayStoreException
in Java's Array
.
Second, there is a parametrized class IntQueue
generated by Queue
with specific type being Int
.
Questions – why the 1) and 2) compiler errors happen
ad 1)
math.sqrt
is defined as def sqrt(x: Double): Double
. My argument's type, x
, is of type Int
. In this case, implicit conversion Int->Long->Float->Double
should happen. Why does the compiler doesn't perform implicit conversion?In your overriding method definition, Int
is not what you expect. You're actually defining a type parameter called Int
but not referring to the "original" Int
type and thus "hiding" the original Int
type.
You actually wrote the same as:
override def enqueue[I](x: I): Queue[I] = ...
Declare it as a non overriding method to achieve what you expect:
def enqueue(x: Int): Queue[Int] = ...