This must be a very basic misunderstanding on my part. It appears that assignments of parametric types are covariant without any indication on my part that that's what I'd want. I'm pasting Scala code for brevity, but it behaves identically in Java.
class Pet
class Fish extends Pet
class Guppy extends Fish
case class Box[T](value: T)
val guppyBox: Box[Fish] = Box(new Guppy()) // Mysteriously, this works.
An instance of type X
can only be assigned to a val of type Y
if Y
is a subtype of X
. In my case, this would require Box to be covariant, which I didn't say it is.
I wouldn't be too hung up on this, but it leads to the following odd, in my view, behavior:
def unboxFish(fish: Box[Fish]) = ???
unboxFish(Box(new Guppy())) // Oddly, compiles ok
val guppyBox2 = Box(new Guppy())
unboxFish(guppyBox2) // The compilation error I'd expect.
Any help greatly appreciated!
CodePudding user response:
Box
isn't covariant. What's happening here is that Box(new Guppy())
needs to infer the type parameter for Box
, and the inference depends on context. When you do
val guppyBox2 = Box(new Guppy())
it infers the type parameter as Guppy
, but when you do
val guppyBox: Box[Fish] = Box(new Guppy())
the compiler knows that the RHS should be a Box[Fish]
, so it infers that the type parameter should be Fish
instead of Guppy
, as if you had written
val guppyBox: Box[Fish] = Box[Fish](new Guppy())
CodePudding user response:
In Scala type inference goes not only from right to left
val guppyBox: Box[??] = Box[Something](...)
but also from left to right
val guppyBox: Box[Something] = Box[??](...)
(so it's bidirectional).
So in
val guppyBox: Box[Fish] = Box(new Guppy())
aka
val guppyBox: Box[Fish] = Box[??](new Guppy())
the type parameter ??
is inferred to be Fish
.
when does it need explicit type when declare variable in scala?
But Box
is now not covariant. Box[Guppy]
is not a subtype of Box[Fish]
implicitly[Box[Guppy] <:< Box[Fish]] // doesn't compile
You can't assign a value of type Box[Guppy]
to a variable of type Box[Fish]
val guppyBox: Box[Fish] = Box[Guppy](new Guppy()) // doesn't compile