DEV Community

Venkatesh-Prasad Ranganath
Venkatesh-Prasad Ranganath

Posted on

2 1

Covariance and Contravariance

In the below Kotlin 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.

class Producer<out T: Any>(val e:T) {
fun read(): T = e
}
class Consumer<in T: Any>() {
private lateinit var e: T
fun write(v: T): Unit { e = v }
}
fun main() {
var p1: Producer<Number> = Producer<Double>(0.4)
p1.read()
var p2: Producer<Double> = Producer<Number>(3) // Disallowed
p2.read()
var c1: Consumer<Number> = Consumer<Double>() // Disallowed
c1.write(3)
var c2: Consumer<Double> = Consumer<Number>()
c2.write(0.4)
}
view raw variance.kt hosted with ❤ by GitHub

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

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more