Variances in Scala

Bartosz Gajda
Aug 5 · 2 min read
Image for post
Image for post

Scala supports using type parameters to implement a classes and functions that can support multiple types. Type parameters are very useful when creating a generics. However, advanced use cases may require you to specify a constraints on types used. That’s where a Variances help to model the relationships between generic types. This post will cover the topic of type variances in Scala.

What is Variance?

Variance is the correlation of subtyping relationships of complex types and the subtyping relationships of their component types.

Scala-lang.org

In other words, variance allows developers to model the relationships between types and component types. As a consequence, variance allows to create a clean and very usable generic classes that are widely used in various Scala libraries.

Scala supports thee types of variance: covariance, invariance and contravariance. With this in mind, let’s look at each of these in greater detail.

Covariance

abstract class Vehicle {
def name: String
}
case class Car(name: String) extends Vehicle
case class Bus(name: String) extends Vehicle
class VehicleList[T](val vehicle: T)

The Car and Bus classes both inherit from abstract class Vehicle. Considering that class Car has its own collection VehicleList[Car]: is VehicleList[Car] a subtype of VehicleList[Vehicle]? The answer is no. Despite the fact that Car class extends a Vehicle class, the same can't be said about VehicleList[Car] and VehicleList[Vehicle].

val vehicleList: VehicleList[Vehicle] = new VehicleList[Car](new Car("bmw")) // incorrect: type mismatch

The solution to this is to use a covariance. As a consequence, a VehicleList[Car] will be a subtype of VehicleList[Vehicle]. This allows for greater polymorphism with generic types. To make VehicleList class T parameter covariant, just add a little + sign along the type:

class VehicleList[+T](val vehicle: T)val vehicleList: VehicleList[Vehicle] = new VehicleList[Car](new Car("bmw")) // correct

Contravariance

class CarList[T](val car: T)

The CarList class expects to get type parameter T and no other super or sub type. What if we want to use the CarList[Car] class to store the Vehicle class instances as well?

val carList: CarList[Car] = new CarList[Vehicle](new Vehicle("boat")) // incorrect: type mismatch

To address that, instead of a + sign in the type definition, use the - sign.

class CarList[-T](val car: T)val carList: CarList[Car] = new CarList[Vehicle](new Vehicle("boat")) // correct

Invariance

class List[T](val t: T)

Summary

The Startup

Medium's largest active publication, followed by +734K people. Follow to join our community.

Bartosz Gajda

Written by

Software Engineer, Computer Science student and Blogger. Cycling, cooking and enjoying craft beer in free time.

The Startup

Medium's largest active publication, followed by +734K people. Follow to join our community.

Bartosz Gajda

Written by

Software Engineer, Computer Science student and Blogger. Cycling, cooking and enjoying craft beer in free time.

The Startup

Medium's largest active publication, followed by +734K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store