0
0
Kotlinprogramming~3 mins

Why In variance (contravariance) in Kotlin? - Purpose & Use Cases

Choose your learning style9 modes available
The Big Idea

What if you could pass a list of cats to a function expecting animals without any hassle or errors?

The Scenario

Imagine you have a list of animals and you want to pass it to a function that only accepts lists of cats. You try to do this manually by checking and converting each item, but it quickly becomes confusing and error-prone.

The Problem

Manually converting or checking types every time you want to pass data between functions is slow and leads to mistakes. You might accidentally allow wrong types or write lots of repetitive code that is hard to maintain.

The Solution

In variance with contravariance, Kotlin lets you safely use a more general type where a more specific type is expected. This means you can pass a list of cats to a function expecting animals without extra checks, making your code cleaner and safer.

Before vs After
Before
fun feedCats(cats: List<Cat>) { /* feed cats */ }
val animals: List<Animal> = listOf(Dog(), Cat())
// Can't pass animals to feedCats directly
After
fun feedCats(cats: List<out Cat>) { /* feed cats */ }
val cats: List<Cat> = listOf(Cat(), Cat())
feedCats(cats) // Allowed with covariance
What It Enables

You can write flexible functions that accept a range of related types safely, reducing code duplication and errors.

Real Life Example

Think of a pet store app where you want to feed all animals that are cats or their subtypes without rewriting feeding logic for each animal subtype.

Key Takeaways

Manual type checks slow down development and cause bugs.

Covariance allows safe use of more specific types where general ones are expected.

This leads to cleaner, safer, and more flexible Kotlin code.