**Type variances** are a topic which most Java developers don't usually think about, but which can take some getting used to for those transitioning into languages which do make heavy use of them, like Scala.

There are three possible kinds of type variance

- invariance
- covariance
- contravariance

Recall that, in Scala, the notation `T <: S`

means that `T`

`extends`

or "subclasses" `S`

, while `T >: S`

means the converse -- that `S`

extends or subclasses `T`

.

You may notice that `T >: S`

means the same as `S <: T`

, so why are there two different ways to say this? Well, you might want to define both an upper (`<:`

) *and* a lower (`>:`

) bound for your type. For example, if you wrote a class hierarchy that mirrored taxonomic ranks, you might want a type that is larger than `Species`

, but less than `Order`

, in which case you would write `Genus <: T <: Family`

.

You can think of `T <: S`

as meaning "`T`

is a type which is *less than or equal to* type `S`

" (in a subclassing / extending sense), while `T >: S`

means that "`T`

is *greater than or equal to* type `S`

". Since there are no taxonomic ranks *between* `Genus`

and `Family`

, `Genus <: T <: Family`

means that type `T`

can be *either* `Genus`

or `Family`

.

### Variance

It's important to remember that when we talk about *variance*, we're talking about the types *themselves*, but also the "container types" which are parameterized by them.

What this means is that you can't talk about type variance without some parameterized type like `Set[A]`

or `List[+A]`

or `Map[K, +V]`

. We're interested in the parameterized types `Set`

, `List`

, and `Map`

, as they relate to their type parameters.

### Invariance

A parameterized type is *invariant* in its type parameter if there is **no subclassing relationship** when using different type parameters. For example, in Scala, `Set[A]`

is invariant in its type parameter `A`

. This means that

```
A <: B => Set[A] <: Set[B] is false
A >: B => Set[A] >: Set[B] is false
A <: B => Set[A] >: Set[B] is false
A >: B => Set[A] <: Set[B] is false
```

In other words, no matter the relationship between `A`

and `B`

, you cannot substitute a `Set[A]`

for a `Set[B]`

and vice versa. A `Set[String]`

*is not* a `Set[Any]`

, for example, and a `Set[Numeric]`

*is not* a `Set[Int]`

.

If you're coming from a Java background, this is the variance you're probably most familiar with. Java was initially developed without generics (no type parameters!), so invariance was really the only solution.

### Covariance

A parameterized type is *covariant* in its type parameter if there is **a parallel subclassing relationship** when using different type parameters. For example, in Scala, `List[+A]`

is covariant in its type parameter `+A`

. (That's what the `+`

means before the generic type `A`

.) This means that

```
A <: B => List[A] <: List[B] is true
A >: B => List[A] >: List[B] is true
A <: B => List[A] >: List[B] is false
A >: B => List[A] <: List[B] is false
```

This is the more "intuitive" type variance, in my opinion. In this case, we might have a subclassing relationship like `Dog <: Animal`

, because a `Dog`

*is a kind of* `Animal`

. In that case, a `List[Dog] <: List[Animal]`

because a list of dogs is a kind of list of animals.

Wherever we require a `List[Animal]`

, we can provide a `List[Dog]`

, because `List[Dog] <: List[Animal]`

(read as "`List`

of `Dog`

is *a kind of* `List`

of `Animal`

").

### Contravariance

Contravariance is the "opposite" of covariance. A parameterized type is *contravariant* in its type parameter if there is **an anti-parallel subclassing relationship** when using different type parameters. For a parameterized type which is contravariant in its type parameter, the following relationships hold

```
A <: B => List[A] <: List[B] is false
A >: B => List[A] >: List[B] is false
A <: B => List[A] >: List[B] is true
A >: B => List[A] <: List[B] is true
```

This one is a bit more difficult to explain.

Imagine we have a parameterized `Teacher[-S]`

class, where `S`

gives the `Subject`

the `Teacher`

is qualified to teach. The `-`

indicates that `Teacher`

is *contravariant* in its type parameter `S`

.

As for the `Subject`

, `S`

, we might say that `TypeVariance`

is *a kind of* programming concept

```
TypeVariance <: ProgrammingConcept
```

...both of which are `Subject`

s which our `Teacher`

might teach.

If our `Teacher`

is qualified to teach `ProgrammingConcept`

s, she must also be qualified to teach `TypeVariance`

(otherwise, she wouldn't be qualified to teach `ProgrammingConcept`

s).

In other words, wherever we require a teacher who is qualified to teach `TypeVariance`

, we can substitute a teacher who is qualified to teach `ProgrammingConcept`

s in general

```
Teacher[ProgrammingConcept] <: Teacher[TypeVariance]
```

Since `TypeVariance`

is *a kind of* `ProgrammingConcept`

, a `Teacher`

of `ProgrammingConcept`

s is a `Teacher`

of `TypeVariance`

.

## Top comments (0)