In Iterable.scala one can read for +C, the type of what tail returns that: "We require that for all child classes of Iterable the variance of the child class and the variance of the C parameter passed to IterableOps are the same. We cannot express this since we lack variance polymorphism. That's why we have to resort at some places to write C[A @uncheckedVariance]."
Now as to what are valid uses for this annotation, let's consider the following code:
class X[+T] {
var ref_ : Any = null
def ref:T = ref_.asInstanceOf[T]
def ref_=(ref: T@uncheckedVariance): Unit = ref_ = ref
}
Without @uncheckedVariance, it wouldn't compile because covariant T appears in contravariant position. Now if Y[+T] extends X[+T] and B extends A then Y[B] extends X[A] and you can write:
val y: Y[B] = null
val x : X[A] = y
Which means you cannot write:
y.ref = new A{}
But you can write, despite x being y:
x.ref = new A{}
That means that when you define Y[B], you most likely don't intend it to be passed for ref some A which lacks the specificities of B, and should some A sneaks its way notheless, you would have a nasty bug. That's why variance is checked by default. Example of code that throws a ClassCastException:
val y = new Y[B]
val x : X[A] = y
x.ref = new A
y.ref.b() // b() in B but not in A
However sometimes you know what you're doing and are absolutely certain that nothing of the sort can happen. Or maybe your documentation explicitly warns the potential user, @uncheckedVariance being already a clear warning. In my experience there are four circumstances in which you may use this annotation.
- When you are working within a private or protected scope, with more control over what's used and how. Perfectly valid.
- When you're extending code that is intended to be used that way, or implementing a pattern that explicitly requires it. Also perfectly valid.
- When you desperately need it because covariance is in your way, the same way you may use setAcessible(true) to bypass a scope limitation. Not valid, but may help, especially when trying to figure things out and as a temporary relief to let the code compile in the process of a complex refactoring.
- When it makes your API significantly more convenient, with the benefit of covariance at a minor risk of miss-use. No less valid than the Java equivalent, generics don't have to be perfectly safe. For instance if your classes are mostly read from and covariance is great, but sometimes they are written to, and covariance is a pain, it's acceptable for writting functions to require extra checking.