0
0
Kotlinprogramming~7 mins

In variance (contravariance) in Kotlin

Choose your learning style9 modes available
Introduction

In Kotlin, contravariance helps you use a more general type where a more specific type is expected. It makes your code flexible and safe when working with types that consume data.

When you want to pass a function or class that accepts a more general type than the one expected.
When you have a consumer (like a function parameter) that only takes input but never produces output.
When working with generic types that only consume values, such as comparators or event handlers.
When you want to avoid type errors by allowing a supertype to be used where a subtype is expected.
Syntax
Kotlin
interface Consumer<in T> {
    fun consume(item: T)
}

The in keyword before the generic type T marks it as contravariant.

Contravariant types can only be used as input (parameters), not as output (return types).

Examples
This interface consumes items of type T. The in keyword means it can accept T or any supertype of T.
Kotlin
interface Consumer<in T> {
    fun consume(item: T)
}
This function accepts a consumer of Int. Thanks to contravariance, you can pass a Consumer<Number> safely.
Kotlin
fun feedConsumer(consumer: Consumer<Int>) {
    consumer.consume(42)
    consumer.consume(100)
}
Because of contravariance, a Consumer<Number> can be assigned to a Consumer<Int> variable.
Kotlin
val numberConsumer: Consumer<Number> = object : Consumer<Number> {
    override fun consume(item: Number) {
        println("Consumed: $item")
    }
}

val intConsumer: Consumer<Int> = numberConsumer
Sample Program

This program shows contravariance by assigning a Consumer to a Consumer variable. The feedConsumer function calls consume with different Int types.

Kotlin
interface Consumer<in T> {
    fun consume(item: T)
}

class NumberConsumer : Consumer<Number> {
    override fun consume(item: Number) {
        println("Consumed Number: $item")
    }
}

fun feedConsumer(consumer: Consumer<Int>) {
    consumer.consume(10)
    consumer.consume(20)
}

fun main() {
    val numberConsumer = NumberConsumer()
    // Contravariance allows this assignment
    val intConsumer: Consumer<Int> = numberConsumer
    feedConsumer(intConsumer)
}
OutputSuccess
Important Notes

Contravariant types cannot be returned from functions because they are only safe as inputs.

Use in only when the generic type is used as a function parameter, never as a return type.

Contravariance is the opposite of covariance, which uses out and allows returning types.

Summary

Use in to mark a generic type as contravariant, meaning it can accept supertypes safely.

Contravariance is useful for consumers that only take input and never produce output.

It helps make your Kotlin code more flexible and type-safe when working with generic types.