Covariance failure for a mutable collection 2026-10-31
Suppose a mutable collection of narrow-type values could be viewed as a mutable collection of a wider type. Inserting a wider-only value through that alias would make a subsequent narrow-type read invalid. For example, hypothetical covariance would let one insert a string through an
Any-typed alias of an integer multiset. Generic type invariance prevents that substitution; making a parameter covariant does not authorize an unsound consuming method. Past exam of the mathematics course of the University of Cambridge 2007 ia Paper 5 3 c Solution Created 2026-09-24 Updated 2026-10-14
The inference is false in both directions. Although
String is a subtype of Object, generic type invariance means that neither Vector<String> nor Vector<Object> is a subtype of the other. If a string vector could be used as an object vector, an arbitrary object could be inserted into storage promised to contain strings. Conversely, an object vector may already contain an Integer, so it cannot satisfy the contract of a string vector.Both parameterizations are subtypes of the ordinary class
Object, but this does not create a subtype relation between their element parameterizations. For a view accepting vectors with different element types, Vector<?> is appropriate; a Vector<? extends Object> similarly allows retrieval as Object without arbitrary non-null insertion. A Vector<? super String> accepts insertion of strings but does not promise that retrieved elements are strings. The stated subclass relationship does not hold: mutable generic vectors are invariant.If the mutable set were allowed a covariant type parameter, both variables below could refer to the same mutable object:The broad view accepts any value, including a string. The narrow view still promises that deletion returns an
val s1: Set[Int] = new Set[Int](List())
val s2: Set[Any] = s1 // hypothetically permitted
s2.add("not an integer")
val n: Int = s1.delete() // cannot return an IntInt, yet the sole stored value is that string. This violates type safety:This is covariance failure for a mutable collection. The input occurrence in add(x: T) conflicts with the positive occurrence in the result of delete. Keeping a generic type invariance restriction prevents the unsafe aliasing. The example uses an initially empty set so that the problematic deletion result is forced.Immutable Scala
List[+A] is covariant: if Juice is a subtype of Drink, a list of juices can be used as a list of drinks. Scala Array[A] is invariant because it permits both reading and writing elements.If an array of juices could be treated as an array of drinks, writing a beer through that widened reference would violate the original juice-array contract. This illustrates covariant type parameter versus generic type invariance; immutability permits the first relationship for lists.
A function accepting every
Base can safely be used where only Derived inputs will be supplied. The reverse direction is unsafe. A Derived argument is already a valid Base argument; restricting the possible callers introduces no new obligation for the function. This is the contravariant input rule of function subtyping.The printed trait has generic type invariance, so Scala does not directly permit either parameter-changing assignment. The safe behavior can be exposed using an explicit adapter without changing that trait:
def restrictInput(f: Fun[Base, Base]): Fun[Derived, Base] =
new Fun[Derived, Base] {
def apply(x: Derived): Base = f(x)
}For the unsafe direction, take illustrative classes in which a derived software object has an extra operation:
class Base
class Derived extends Base { def special(): Int = 1 }
val narrow = new Fun[Derived, Base] {
def apply(x: Derived): Base = { x.special(); x }
}
def broadClient(f: Fun[Base, Base]): Base = f(new Base)
// broadClient(narrow) must be rejected.If that substitution were accepted, the client could pass an ordinary base software object to code that calls
special, which that software object does not support. The rejected call is intentional counterexample code, not a promised compilable application. Variance annotations are needed to make sound direct conversions part of the generic trait's declared subtyping relation.[ref]Scala 2.13 variance annotations specification. Variance Annotations: missing annotation is invariant; method-input positions reverse variance
Type variance 2026-10-24
How subtyping between parameter types affects subtyping between instances of a parameterized type. A covariant type parameter preserves the direction, a contravariant type parameter reverses it, and generic type invariance permits neither conversion solely from the parameter relationship. Safe variance depends on how a type is consumed, produced and stored.