I'm trying to use a covariant type parameter inside a trait to construct a case-class like so:
trait MyTrait[+T] {
private case class MyClass(c: T)
}
compiler says:
error: covariant type T occurs in contravariant position in type T of value c
I then tried the following but it also didn't work:
trait MyTrait[+T] {
private case class MyClass[U <: T](c: U)
}
the error this time is:
error: covariant type T occurs in contravariant position in type >: Nothing <: T of type U
Could somebody explain why the T is in a covariant position here and suggest a solution for this problem? Thx!
Almost there. Here:
Which means
MyClass[Any]
is valid for allT
. That is at the root of why one cannot useT
in that position, but demonstrating it requires more code than I'm in the mood for at the moment. :-)This is a fundamental feature of object-oriented programming that doesn't get as much attention as it deserves.
Suppose you have a collection
C[+T]
. What the+T
means is that ifU <: T
, thenC[U] <: C[T]
. Fair enough. But what does it mean to be a subclass? It means that every method should work that worked on the original class. So, suppose you have a methodm(t: T)
. This says you can take anyt
and do something with it. ButC[U]
can only do things withU
, which might not be all ofT
! So you have immediately contradicted your claim thatC[U]
is a subclass ofC[T]
. It's not. There are things you can do with aC[T]
that you can't do with aC[U]
.Now, how do you get around this?
One option is to make the class invariant (drop the
+
). Another option is that if you take a method parameter, to allow any superclass as well:m[S >: T](s: S)
. Now ifT
changes toU
, it's no big deal: a superclass ofT
is also a superclass ofU
, and the method will work. (However, you then have to change your method to be able to handle such things.)With a case class, it's even harder to get it right unless you make it invariant. I recommend doing that, and pushing the generics and variance elsewhere. But I'd need to see more details to be sure that this would work for your use case.