Covariance and Contravariance
Tags: programming, programminglanguages, kotlin, coding
Published: 2019-04-21 04:59:23

---
title: Covariance and Contravariance
published: true
description: Understanding co- and contra-variances in programming
tags: Programming, Programming Languages, Kotlin, Coding
---
In the below [Kotlin](https://kotlinlang.org/) code snippet, *Producer class is covariant in type parameter T* and *Consumer class is contravariant in type parameter T*. In other words, *T is a covariant type parameter of Producer* and *T is a covariant type parameter of Consumer*. So, what do these statements mean?

To understand the meaning of these statements, let’s consider the allowed behaviors. Copy-n-paste the above code snippet at https://try.kotlinlang.org and run it. You will observe two errors — at lines 13 and 15.

{% gist https://gist.github.com/rvprasad/d6cabc815313051d3055e28fa75c251f %}

## Covariance

The assignment on line 11 is allowed as `Producer<Double>` is treated as a sub-type of `Producer<Number>`. This is due to two reasons.

- Values of type `T` can *only be retrieved/read from* `Producer<T>` instances because `T` is used *to specify only the values provided by* `Producer`; see the class definition of `Producer`.
- Since `Producer<Double>` instance is assigned to `p1`, all values retrieved/read via `p1` will be of type `Double`, a sub-type of the type argument `Number` of the declared type `Produce<Number>` of `p1`. So, the behavior exhibited by the instance assigned to `p1` conforms with (is a subset of) the behavior guaranteed by the declared type of `p1`.

Due to the above sub-typing relation, the assignment on line 13 is not allowed. If it was allowed, `p2.read()` would return a value of type `Number` when it should return only values of type `Double` based on the declared type `Producer<Double>` of `p2`.

## Contravariance

A similar reasoning explains contravariance.

The assignment on line 17 is allowed as `Consumer<Number>` is treated as a sub-type of `Consumer<Double>`. This is due to two reasons.

- Values of type `T` can *only be injected/written into* `Consumer<T>` instances because `T` is used *to specify only the values provided to* `Consumer`; see the class definition of `Consumer`.
- Since the type argument `Double` of the declared type of `c2` is a sub-type of `Number`, all values provided to `Consumer<Number>` instance assigned to `c2` will be a sub-type of type `Number`. The behavior allowed by the declared type of `c2` conforms with (is a subset of) the behavior supported by the instance assigned to `c2`.

Due to the above sub-typing relation, the assignment on line 15 is not allowed. If it was allowed, `c1.write(3)` would store a value of type `Number` in the property (field) `c1.e` when it should store only values of type `Double` in `c1.e` based on the `Consumer<Double>` instance assigned to `c1`.

## Summary

In short, if type `X` is a sub-type of type `Y` and type `S<X>` can be treated as sub-type of type `S<Y>`, then `S` is **covariant** in `T` and `T` is a **covariant type parameter** of the generic type `S<T>`.

Similarly, if type `X` is a sub-type of type `Y` and `S<Y>` can be treated as sub-type of type `S<X>`, then `S` is **contravariant** in `T` and `T` is a **contravariant type parameter** of the generic type `S<T>`.

In both definitions, the key is if `S<X>` will be treated as sub-type of `S<Y>` or vice versa, and this  is dependent on how the type parameter `T` of `S<T>` is used in `S`; specifically, in the context of input and output of the operations/methods of `S`.

## Further Reading

- https://kotlinlang.org/docs/reference/generics.html#variance
- Chapter 11 of [Fundamentals of Object-Oriented Design in UML](https://www.amazon.com/Fundamentals-Object-Oriented-Design-Meilir-Page-Jones/dp/020169946X/)