flipped at the type argument position of a type, such as the Arg in C[Arg], depending on the variance of the corresponding type parameter. If C’s type parameter is annotated with a + then the classification stays the same. If C’s type parameter is annotated with a -, then the current classification is flipped. If C’s type parameter has no variance annotation then the current classification is changed to neutral.
As a somewhat contrived example, consider the following class definition, where the variance of several positions is annotated with + (for positive) or (for negative):
abstract class Cat[-T, +U] {
def meow[W ](volume: T , listener: Cat[U+, T ] ) : Cat[Cat[U+, T ] , U+]+
}
The positions of the type parameter, W, and the two value parameters, volume and listener, are all negative. Looking at the result type of meow, the position of the first Cat[U, T] argument is negative, because Cat’s first type parameter, T, is annotated with a -. The type U inside this argument is again in positive position (two flips), whereas the type T inside that argument is still in negative position.
You see from this discussion that it’s quite hard to keep track of variance positions. That’s why it’s a welcome relief that the Scala compiler does this job for you.
Once the variances are computed, the compiler checks that each type parameter is only used in positions that are classified appropriately. In this case, T is only used in negative positions, and U is only used in positive positions. So class Cat is type correct.
19.5 Lower bounds
Back to the Queue class. You saw that the previous definition of Queue[T] shown in Listing 19.4 cannot be made covariant in T because T appears as a type of a parameter of the enqueue method, and that’s a negative position.
Fortunately, there’s a way to get unstuck: you can generalize enqueue by making it polymorphic (i.e., giving the enqueue method itself a type parameter) and using a lower bound for its type parameter. Listing 19.6 shows a new formulation of Queue that implements this idea.