codingstuff.io
ExploreTutorialsProblemsCS Subjects
Get Started
ExploreTutorialsProblemsCS Subjects
Get Started
codingstuff.io

Master the art of building software through interactive tutorials, real-world problems, and guided projects.

Pune, Maharashtra, India

codingstuffmail@gmail.com

Product

  • Explore
  • Tutorials
  • Problems
  • CS Subjects

Company

  • About
  • Contact
  • Privacy Policy
  • Terms & Conditions
  • Sitemap

© 2026 codingstuff.io. All rights reserved.

Built with ❤️ for developers everywhere

/
/
All Tutorials
🎯

Kotlin

18 / 68 topics
11Extensions Functions12Classes and Objects13Inheritance in Kotlin14Interfaces in Kotlin15Data Classes16Sealed Classes17Companion Objects18Generics in Kotlin19Type Aliases20Properties and Fields21Lateinit Properties22Delegated Properties
Tutorials/Kotlin/Generics in Kotlin
🎯Kotlin

Generics in Kotlin

Updated 2026-04-20
3 min read

Introduction to Generics

Generics are a powerful feature of Kotlin that allow you to write flexible and reusable code by parameterizing types. They enable you to create classes, interfaces, and functions that operate on objects of various types while providing type safety at compile time. This tutorial will explore the fundamentals of generics in Kotlin, including their syntax, use cases, and best practices.

Basic Syntax

In Kotlin, generics are defined using angle brackets (<>). You can declare a generic class or function by specifying one or more type parameters within these brackets.

Generic Classes

class Box<T>(var content: T) {
    fun getContent(): T = content
}

In this example, Box is a generic class with a single type parameter T. The variable content holds an object of type T, and the method getContent() returns that object.

Generic Functions

fun <T> printElement(element: T) {
    println("Element: $element")
}

Here, printElement is a generic function that takes a parameter of type T and prints it. The type parameter <T> is specified before the function name.

Type Constraints

Type constraints allow you to restrict the types that can be used as type arguments for a generic class or function. You can specify an upper bound using the : operator.

class Container<T : Number>(var value: T) {
    fun getValue(): Double = value.toDouble()
}

In this example, Container is a generic class that only accepts types that are subclasses of Number. The method getValue() converts the stored value to a Double.

Multiple Type Constraints

You can also specify multiple type constraints using the where keyword.

fun <T> copyWhenGreater(list: List<T>, threshold: T): List<T>
    where T : Number, T : Comparable<T> {
    return list.filter { it > threshold }
}

Here, copyWhenGreater is a generic function that filters elements in the list greater than the given threshold. It requires T to be both a Number and Comparable.

Invariant Generics

By default, generics in Kotlin are invariant, meaning you cannot assign a List<SubClass> to a variable of type List<SuperClass>. This ensures type safety but can sometimes be restrictive.

open class Animal
class Dog : Animal()

fun processAnimals(animals: List<Animal>) {
    // Process animals
}

val dogs: List<Dog> = listOf(Dog())
// processAnimals(dogs) // Error: Type mismatch

Covariant Generics

Covariance allows a generic type to be used in a more general context. In Kotlin, you can achieve covariance using the out keyword.

fun <T> feedAnimals(animals: List<out Animal>) {
    for (animal in animals) {
        println("Feeding ${animal::class.simpleName}")
    }
}

val dogs: List<Dog> = listOf(Dog())
feedAnimals(dogs) // Valid

In this example, List<out Animal> is a covariant list of Animal or its subclasses. The function feedAnimals can accept any list of animals.

Contravariant Generics

Contravariance allows a generic type to be used in a more specific context. In Kotlin, you can achieve contravariance using the in keyword.

fun <T> addDogs(dogList: MutableList<in Dog>, dog: T) where T : Dog {
    dogList.add(dog)
}

val animals: MutableList<Animal> = mutableListOf(Animal())
addDogs(animals, Dog()) // Valid

Here, MutableList<in Dog> is a contravariant mutable list of Dog or its superclasses. The function addDogs can add any subclass of Dog to the list.

Generic Functions with Reified Types

Reified types allow you to access the type information at runtime within inline functions. This feature is particularly useful for generic functions that need to perform operations based on the actual type of the argument.

inline fun <reified T> createInstance(): T {
    return when (T::class) {
        String::class -> "Hello" as T
        Int::class -> 42 as T
        else -> throw IllegalArgumentException("Unsupported type")
    }
}

val str: String = createInstance()
val num: Int = createInstance()

In this example, createInstance is an inline function with a reified type parameter. It creates and returns an instance of the specified type.

Best Practices

  1. Use Generics Sparingly: Only use generics when they provide significant benefits in terms of code reuse and safety.
  2. Keep Type Parameters Minimal: Use only as many type parameters as necessary to achieve your goals.
  3. Avoid Overly Restrictive Constraints: Be careful not to over-constrain your generic types, which can limit their usefulness.
  4. Use Covariance and Contravariance Wisely: Leverage covariance and contravariance to write flexible code that adheres to the Liskov Substitution Principle.

Conclusion

Generics are a powerful feature in Kotlin that enhance code flexibility and safety by allowing you to parameterize types. By understanding their syntax, constraints, variance, and reified types, you can write more robust and reusable code. This tutorial has provided a comprehensive guide to generics in Kotlin, covering both basic and advanced concepts.


PreviousCompanion ObjectsNext Type Aliases

Recommended Gear

Companion ObjectsType Aliases