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.
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:
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 Int
The broad view accepts any value, including a string. The narrow view still promises that deletion returns an Int, 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]
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.