0
0
KotlinConceptBeginner · 4 min read

What is Variance in Kotlin Generics Explained Simply

In Kotlin, variance controls how generic types relate to each other when their type parameters differ. It uses out (covariant) and in (contravariant) keywords to allow safe type substitution, making generic types flexible and type-safe.
⚙️

How It Works

Imagine you have a box that can hold items of a certain type. Variance in Kotlin tells us how we can safely replace that box with another box holding a related type. For example, if you have a box of apples, can you use it where a box of fruits is expected? Variance answers this question.

Kotlin uses two main keywords: out and in. The out keyword means the generic type only produces or returns values (like a fruit basket you only take fruits from). This is called covariance and allows you to use a more specific type where a general type is expected.

The in keyword means the generic type only consumes or accepts values (like a basket where you only put fruits in). This is contravariance and allows you to use a more general type where a specific type is expected. This system keeps your code safe and flexible.

💻

Example

This example shows how out and in work in Kotlin generics.

kotlin
open class Fruit
class Apple : Fruit()

class Box<out T>(val item: T) {
    fun getItem(): T = item
}

fun feedFruit(box: Box<Fruit>) {
    println("Feeding fruit: ${box.getItem()}")
}

fun main() {
    val appleBox: Box<Apple> = Box(Apple())
    feedFruit(appleBox) // Works because Box is covariant with 'out'
}
Output
Feeding fruit: Apple@<hashcode>
🎯

When to Use

Use variance when you want to make your generic classes or interfaces more flexible and safe with type substitutions. Use out when your generic type only produces or returns values, like a read-only list or a data source. Use in when your generic type only consumes or accepts values, like a consumer or a sink.

For example, if you have a function that only reads data from a collection, declare the collection as out to accept subtypes safely. If you have a function that only writes data into a collection, declare it as in to accept supertypes safely.

Key Points

  • Covariance (out): allows a generic type to produce values and be substituted with its subtypes.
  • Contravariance (in): allows a generic type to consume values and be substituted with its supertypes.
  • Variance helps keep code type-safe while allowing flexible use of generic types.
  • Use out for read-only or producer roles, and in for write-only or consumer roles.

Key Takeaways

Variance controls how generic types relate when their type parameters differ.
Use 'out' for covariant types that produce values safely.
Use 'in' for contravariant types that consume values safely.
Variance makes generics flexible and type-safe in Kotlin.
Apply variance to improve code reuse and prevent type errors.