0
0
KotlinConceptBeginner · 3 min read

What is in Keyword in Kotlin Generics Explained

In Kotlin generics, the in keyword is used to declare a type parameter as contravariant, meaning it can accept a supertype of the specified type. This allows a generic class or function to safely consume values of that type or its subtypes, enabling flexible and type-safe input handling.
⚙️

How It Works

Imagine you have a box that only takes items of a certain type. The in keyword in Kotlin generics tells the box it can accept items of the specified type or any of its supertypes. This is called contravariance.

Contravariance means you can use a more general type where a more specific type is expected. For example, if a function expects a box that can accept Number, you can give it a box that accepts Any because Any is a supertype of Number. This helps keep your code flexible and safe.

In Kotlin, in is used only for input positions (where values go into a generic type), ensuring you don't accidentally output values of the wrong type.

💻

Example

This example shows a generic interface using in to accept values of type T or its supertypes safely.

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

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

fun feedNumbers(consumer: Consumer<Number>) {
    consumer.consume(42)
    consumer.consume(3.14)
}

fun main() {
    val anyConsumer: Consumer<Any> = object : Consumer<Any> {
        override fun consume(item: Any) {
            println("Consumed any: $item")
        }
    }

    // This line will cause a type mismatch error because Consumer<Any> is not a subtype of Consumer<Number>
    // feedNumbers(anyConsumer) // Uncommenting this will cause a compilation error

    val numberConsumer = NumberConsumer()
    feedNumbers(numberConsumer) // Works
}
Output
Consumed number: 42 Consumed number: 3.14
🎯

When to Use

Use the in keyword when you want a generic type to only consume values (input) and not produce them (output). This is common in cases like event handlers, consumers, or processors where you only pass data in.

For example, if you have a function that accepts a generic consumer of data, marking the type parameter with in allows you to pass consumers that accept broader types safely. This increases flexibility without losing type safety.

Key Points

  • in marks a generic type as contravariant, allowing supertypes as input.
  • It ensures type safety by restricting the generic type to input positions only.
  • Commonly used for consumers or handlers that only take values.
  • Improves flexibility by allowing broader types to be passed where narrower types are expected.

Key Takeaways

The in keyword makes a generic type contravariant, accepting supertypes as input.
Use in when your generic type only consumes values, not produces them.
It helps write flexible and type-safe code for consumers and handlers.
Contravariance with in restricts usage to input positions to avoid type errors.