What if you could pass a list of cats to a function expecting animals without any hassle or errors?
Why In variance (contravariance) in Kotlin? - Purpose & Use Cases
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.
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.
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.
fun feedCats(cats: List<Cat>) { /* feed cats */ }
val animals: List<Animal> = listOf(Dog(), Cat())
// Can't pass animals to feedCats directlyfun feedCats(cats: List<out Cat>) { /* feed cats */ }
val cats: List<Cat> = listOf(Cat(), Cat())
feedCats(cats) // Allowed with covarianceYou can write flexible functions that accept a range of related types safely, reducing code duplication and errors.
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.
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.