# Scala Variances: Covariance, Contravariance, and Invariance

## 1. Variance

Variance is the interconnection of subtyping relationship between complex types and their component types.

Variance is all about sub-typing. It tells us if a type constructor is a subtype of another type constructor. Variance defines inheritance relationships of parameterized types(types that have parameters within them).

### Sub-Typing

Every programming language supports the concept of types. Types give information about how to handle values at runtime. Subtyping adds more constraints to the values of a type.

Let’s see an example of subtyping:

``sealed trait Polygoncase object Parallelogram extends Polygoncase object Rectangle extends Parallelogramcase object Square extends Rectangle``

The type `Square` is a subtype of `Rectangle`, which is a subtype of `Parallelogram`, which is a subtype of trait `Polygon`.

### Type Constructor and Parametrized Types

Scala supports generic types or type constructors to reuse code for many types at once. Type constructors are a mechanism that provides type variables that we can bind to concrete types.

Here `T` is known as Type Parameter and `List[T]` is known as Generic or Type Constructor.

For `List[T]`, if we use `List[Int]`, `List[AnyVal]`, etc. then these `List[Int]` and `List[AnyVal]` are known as Parameterized Types.

## 2. Types of Variance

Scala supports three types of variance:

• Covariance (Preserved)
• Contravariance (Reversed)
• Invariance (Ignored)

Scala supports variance annotations of type parameters of generic classes, to allow them to be covariant, contravariant, or invariant(if no annotations are used).

### 2.1. Covariance

If `S` is subtype of `T` then `List[S]` is a subtype of `List[T]`.

This kind of inheritance relationship between two parameterized types is known as Covariance.

We declare a covariant type constructor using the following notation:

A type parameter `T` of a generic class can be made covariant by using the annotation `+T`.

Let’s see an example of a covariant type constructor:

``````class Shape[+T](polygon: T)

sealed trait Polygon
case object Parallelogram extends Polygon``````

We defined the type constructor `Shape` as covariant, which means that the type `Shape[Parallelogram]` is a subtype of `Shape[Polygon]`. The covariance property allows us to declare a variable like:

``val shape: Shape[Polygon] = new Shape[Parallelogram](List(new Parallelogram))``

Every time we need to assign a variable of type `Shape[T]`, we can use an object of type `Shape[R]`, given that `R`is a subtype of `T`.

Covariance is type-safe because it reflects the standard behavior of subtyping. Assigning an object to a variable of one of its super-types is always safe.

In the above example, if we remove the covariant annotation from the type constructor `Shape[T]`, the compiler warns us that we cannot use an object of type `Parallelogram`.

### 2.2. Contravariance

If `S` is subtype of `T` then `List[T]` is a subtype of `List[S]`.

This relation is contrary to the covariance relation.

This kind of inheritance relationship between two parameterized types is known as Contravariance.

We declare a contravariant type constructor using the following notation:

A type parameter `T` of a generic class can be made contravariant by using the annotation `-T`.

Let’s see an example of a contravariant type constructor:

``````class Shape[+T](polygon: T)

case object Rectangle
case object Square extends Rectangle``````

We defined the type constructor `Shape` as contravariant, which means that the type `Shape[Rectangle]` is a subtype of `Shape[Square]`. The contravariance property allows us to declare a variable like:

``val shape: Shape[Square] = new Shape[Rectangle](List(new Rectangle))``

Every time we need to assign a variable of type `Shape[T]`, we can use an object of type `Shape[R]`, given that `T` is a subtype of `R`.

### 2.3. Invariance

If `S` is subtype of `T` then `List[S]` and `List[T]` don’t have inheritance relationship or sub-typing. That means both are unrelated.

Generic classes in Scala are invariant by default. This means that they are neither covariant nor contravariant.

We say that a type constructor `F[_]` is invariant if any subtype relationship between types `A` and `B` is not preserved in any order between types `F[A]`and `F[B]`.

This kind of relationship between two parameterized types is known as Invariance or Non-Variance.

Let’s see an example of an invariant type constructor:

``````class Shape[T](polygon: T)

case object Parallelogram
case object Rectangle extends Parallelogram``````

The `Shape[Parallelogram]` accepts only `Parallelogram` type. Any super type or sub type is not accepted.

``val suite: Shape[Parallelogram] = new Shape[Parallelogram](List(new Parallelogram))``

## 3. Variance and Liskov Substitution Principle

Variance is related to the Liskov Substitution Principle (LSP) telling that “functions that use pointers to base classes must be able to use objects of derived classes without knowing it”.

This means that if `S` is a subtype of `T`, then the objects of type `T` maybe replaced with objects of type `S` without changing the behavior of `T`.

## 4. Conclusion

We looked at the three types of variance: covariance, contravariance, and invariance.