Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Advanced Generics in Kotlin

Introduction

Generics in Kotlin allow developers to create classes, interfaces, and functions with type parameters. This capability enables code reusability and type safety. In this tutorial, we will explore advanced concepts of generics, including variance, type projections, and generic constraints.

Variance

Variance in Kotlin refers to how subtyping between more complex types relates to subtyping between their component types. There are two types of variance: covariance and contravariance.

Covariance

Covariance allows a generic type to be a subtype of another type if its type parameter is a subtype of another type. You can use the out keyword to declare a covariant type parameter.

Example of Covariance:

interface Producer { fun produce(): T }

class FruitProducer: Producer { override fun produce() = Fruit() }

class AppleProducer: Producer { override fun produce() = Apple() }

val producer: Producer = AppleProducer()

Contravariance

Contravariance allows a generic type to be a supertype of another type if its type parameter is a supertype of another type. You can use the in keyword to declare a contravariant type parameter.

Example of Contravariance:

interface Consumer { fun consume(item: T) }

class FruitConsumer: Consumer { override fun consume(item: Fruit) {} }

class AppleConsumer: Consumer { override fun consume(item: Apple) {} }

val consumer: Consumer = FruitConsumer()

Type Projections

Type projections allow you to use a generic type without knowing its exact type parameter. You can use the out and in keywords to define how you can use the type parameter.

Example of Type Projections:

fun printProducer(producer: Producer) { println(producer.produce()) }

val appleProducer: Producer = AppleProducer()

printProducer(appleProducer)

Generic Constraints

Generic constraints allow you to specify conditions on type parameters. You can ensure that a type parameter is a subtype of a particular class or implements a specific interface.

Example of Generic Constraints:

fun add(a: T, b: T): T { return (a.toDouble() + b.toDouble()) as T }

val sum: Double = add(5.0, 10.0)

val intSum: Int = add(5, 10)

Conclusion

Advanced generics in Kotlin provide powerful tools for building flexible and reusable code. Understanding concepts like variance, type projections, and generic constraints can help you write more type-safe and expressive code. As you continue to explore Kotlin, use these advanced features to improve your code quality and maintainability.