Generics in Kotlin: why they are needed and how to use them for beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Generics in Kotlin

\n\n

When developing in Kotlin, sooner or later you will encounter a situation where the same algorithm or data structure must work with different types of objects. Imagine you are writing a function to find the maximum element in a list — it should handle both a list of numbers and a list of strings equally well. Without special tools, you would have to write separate implementations for each type. This is where Generics come to the rescue.

\n\n

Generics in Kotlin are a powerful mechanism that allows you to create classes, functions, and interfaces that work with different data types while maintaining strict type checking at compile time. In this article, we will break down why Generics are needed and learn how to use them with practical examples.

\n\n

What are Generics and why are they needed?

\n\n

Imagine a box that can store any item: a book, a phone, or an apple. In programming, such a "box" is a generic class or function. Without Generics, you would have to use the Any type (similar to Object in Java) and constantly perform type casting, which is prone to errors.

\n\n

The problem without Generics

\n\n
fun <T> printList(list: List<T>) {\n    for (item in list) {\n        println(item)\n    }\n}\n\n// Without Generics you would have to write it like this:\nfun printListAny(list: List<Any>) {\n    for (item in list) {\n        // Need to check the type and cast\n        if (item is String) {\n            println("String: $item")\n        } else if (item is Int) {\n            println("Number: $item")\n        }\n    }\n}
\n\n

Advantages of using Generics

\n\n
    \n
  • Type safety — the compiler checks the correctness of type usage at compile time.
  • \n
  • Code reuse — one function or class works with any types.
  • \n
  • No explicit casts — the code becomes cleaner and less error-prone.
  • \n
  • Performance — no overhead from type checking at runtime.
  • \n
\n\n

Basic Generics constructs in Kotlin

\n\n

1. Generic functions

\n\n

A generic function is a function that accepts a type parameter. It is declared using angle brackets <T> before the function name.

\n\n
fun <T> getFirstElement(list: List<T>): T? {\n    return list.firstOrNull()\n}\n\nfun main() {\n    val numbers = listOf(1, 2, 3, 4, 5)\n    val firstNumber = getFirstElement(numbers)\n    println("First number: $firstNumber") // 1\n\n    val words = listOf("Cat", "Dog", "Parrot")\n    val firstWord = getFirstElement(words)\n    println("First word: $firstWord") // Cat\n}
\n\n

Note: the type T is automatically inferred by the compiler. You can specify it explicitly, but it is not necessary:

\n\n
val firstNumber = getFirstElement<Int>(numbers)
\n\n

2. Generic classes

\n\n

A generic class is a class that can store and process objects of any type. A great example is the standard Box class.

\n\n
class Box<T>(private val content: T) {\n    fun getContent(): T {\n        return content\n    }\n\n    fun isTypeOf(typeClass: Class<*>): Boolean {\n        return typeClass.isInstance(content)\n    }\n}\n\nfun main() {\n    val stringBox = Box("Hello, world!")\n    println(stringBox.getContent()) // Hello, world!\n\n    val intBox = Box(42)\n    println(intBox.getContent()) // 42\n\n    // Type check\n    println(intBox.isTypeOf(Int::class.java)) // true\n}
\n\n

3. Generic interfaces

\n\n

Interfaces can also be generic. This is especially useful when creating collections or contracts for data processing.

\n\n
interface Repository<T> {\n                

Blogs

Book Recommendations