From dca477501146a251b596af2b09b703d698b88f78 Mon Sep 17 00:00:00 2001 From: Jamie B <53781962+JamieB-gu@users.noreply.github.com> Date: Mon, 6 Jul 2026 14:39:14 +0100 Subject: [PATCH] Add "variance" to generics slides The slides now cover this directly rather than linking to the docs. --- scala/original-docs/4-generics/slides.md | 116 ++++++++++++++++++++++- 1 file changed, 112 insertions(+), 4 deletions(-) diff --git a/scala/original-docs/4-generics/slides.md b/scala/original-docs/4-generics/slides.md index 7995eeb..739c126 100644 --- a/scala/original-docs/4-generics/slides.md +++ b/scala/original-docs/4-generics/slides.md @@ -344,11 +344,119 @@ Here the `<:` operator says that `A` has to be a sub-type of `Seq`, which is a t --- -## Other Generic Constraints +## Type Hierarchy -There are other kinds of generic constraints detailed in the Scala docs: +Imagine an `Animal` trait that has multiple sub-classes representing different types of animals. -- Lower Type Bounds: https://docs.scala-lang.org/tour/lower-type-bounds.html -- Variances: https://docs.scala-lang.org/tour/variances.html +```scala +trait Animal + +class Cat extends Animal +class Dog extends Animal +``` + + +`Cat` and `Dog` are **sub-types** of `Animal`. `Animal` is a **super-type** of both `Cat` and `Dog`. + + +--- + +## Upper and Lower Type Bounds + + +There are two kinds of generic constraints to represent sub-type and super-type relationships. + + +An upper type bound states that `A` must be a sub-type of `Animal`: + +```scala +A <: Animal +``` + +A lower type bound states that `A` must be a super-type of `Animal`: + +```scala +A >: Animal +``` + +--- + +## Invariance + + +Imagine you have a box that can store all kinds of things, so it's defined with a generic type parameter. + + +```scala +class Box[A] +``` + +If a type `Cat` is a sub-type of `Animal`, is a `Box[Cat]` a sub-type of `Box[Animal]`? + + +It's not because, by default, type parameters are **invariant**. This means that two instances of the same generic type, e.g. a `Box`, do not have any sub-typing relationship, even if the types they contain do. + + +--- + +## Covariance + + +To allow generic types to inherit the sub-typing relationship between the types they contain, their type parameters must be made **covariant**. This is represented as a `+` next to the type parameter. + + +```scala +class Box[+A] +``` + +If a type `Cat` is a sub-type of `Animal`, then `Box[Cat]` is a sub-type of `Box[Animal]`. + + +This means that anywhere a `Box[Animal]` is expected, a `Box[Cat]` can be passed. + + +--- + +## Contravariance + + +The opposite of covariance is **contravariance**, represented as a `-` next to the type parameter. + + +```scala +class Box[-A] +``` + +If a type `Cat` is a sub-type of `Animal`, then `Box[Animal]` is a sub-type of `Box[Cat]`. + + +This means that anywhere a `Box[Cat]` is expected, a `Box[Animal]` can be passed. + + +--- + +## Variance + + +In summary, there are three kinds of variance used to determine the sub-typing relationship between generic container types. + + +### Invariant + +`class Box[T]` + +Even if `A` is a sub-type of `B`, `Box[A]` and `Box[B]` have no sub-typing relationship. + +### Covariant + +`class Box[+T]` + +If `A` is a sub-type of `B`, `Box[A]` is a sub-type of `Box[B]`. + +### Contravariant + +`class Box[-T]` + +If `A` is a sub-type of `B`, `Box[B]` is a sub-type of `Box[A]`.