Reputation: 40500
I had a (looong) version of this problem posted yesterday here: Trouble with type variance
To cut the (really) long story short, this:
class A[-P, T <: P]
does not compile (it complains, that "P occurs in covariant position in type <: P of type T").
An answer to my earlier question suggested that the declaration is indeed invalid, because A[Foo, Bar]
would be a subclass of A[Nothing, Bar]
, but the latter is illegal (Bar
isn't a subclass of Nothing
).
I don't think that's reason enough. By that logic, something like this should be illegal too: class B[+T <: String]
- B[String]
should be a subclass of B[Any]
but the latter is invalid.
Moreover, this:
class C[T, -P >: T]
Actually does compile. Isn't this essentially the same exact thing as A
above?
Upvotes: 1
Views: 37
Reputation: 29193
These are subtly different.
class Test[-P, T <: P]
says "let P
be a type parameter that varies contravariantly and unboundedly, and let T
be a subtype of P
." That is, T
is completely ignored when checking whether a type is valid for P
. Only then is T
constrained to be a subtype of P
. This is illegal, for the reason you already know. You can start with Test[Any, String]
, and because P
is unboundedly contravariant, it can become Test[Nothing, String]
, and suddenly you have String <: Nothing
.
class tseT[T, -P :> T] // Code highlighters HATE him. Click to find out why.
says "let T
be a type parameter that doesn't vary and is unbounded, and then let P
be a contravariant type parameter that is lower-bounded by T
." This is tiny bit different, because this time P
's variance is constrained by the lower bound T
. P
still varies contravariantly, but it cannot vary lower than T
, or else it will violate P >: T
. This is safe, because you can start with tseT[String, Any]
, and then P
can only vary down as long as it is still a supertype of String
, so it is illegal for P
to vary down to Nothing
.
Note that type parameters are processed left-to-right. This is why the first example is declared invalid, instead of inferring the bound P >: T
, because T
is not declared yet when P
is processed, and such a bound would be a forward-reference anyway.
Upvotes: 1