liveBook · Manning

Generics

As explained in Kotlin in Action, 2E

A generic function is a function declared with one or more type parameters so it can operate on values of different types.

Generics let functions, classes, and interfaces describe types with placeholders, then replace those placeholders with concrete type arguments. A List<String> records what its elements are; a Map<String, Person> records both key and value types. Kotlin requires those arguments to be explicit or inferred, unlike Java raw types. section 11.1

Write a generic function

Place type parameters before the function name. Here T connects the collection element type to the code that processes each element: fun <T> joinToString( collection: Collection<T>, separator: String, prefix: String, postfix: String ): String { /* append each element */ } The same declaration works for collections of any element type. At a call site, Kotlin normally infers T from the argument, so explicit type arguments are rarely needed. section 3.2 section 11.1.1

A type parameter can appear in a receiver, parameter, return type, lambda type, or generic extension property. In `fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>`, the receiver establishes T and the lambda parameter becomes that same type. Classes use the same angle-bracket syntax: `class ArrayList<T> : List<T>`. section 11.1.1 section 11.1.2

Let inference carry the type

A nonempty factory call supplies enough evidence for inference: val authors = listOf("Sveta", "Seb", "Dima") // List<String> An empty collection needs a type from somewhere: val readers: MutableList<String> = mutableListOf() // or: val readers = mutableListOf<String>() section 11.1

Unbounded Kotlin type parameters accept nullable type arguments: an unbounded T effectively has upper bound `Any?`. Use `T : Any` when callers must supply a non-null type; the compiler then rejects arguments such as `String?` and values such as `null`. section 11.1.4 section 7.11

Type arguments guide compile-time checking but are erased at run time. Code can test `is List<*>`, but normally cannot distinguish `List<String>` from `List<Int>` with an `is` check. Inline reified type parameters are the stated exception for runtime type use. section 11.2.1 section 11.4

Generics also define how related parameterized types interact. `out` makes a producer covariant, allowing `List<String>` where `List<Any>` is expected; `in` makes a consumer contravariant. For individual uses, `out` and `in` projections express read-only or write-only roles, paralleling Java wildcards. section 11.3.3 section 11.3.5

Listing 11.2 Calling a generic higher-order function
fun main() {
   val authors = listOf("Sveta", "Seb", "Roman", "Dima")
   val readers = mutableListOf<String>("Seb", "Hadi")
   println(readers.filter { it !in authors })
   // [Hadi]
}

Inspect how `filter` infers `T` as `String` from a `List<String>` receiver and gives the lambda parameter that type. section 11.1.1

How Kotlin in Action, 2E covers it

How deep it goes
Explains declarations, inference, nullability bounds, erasure, runtime limits, and declaration- and use-site variance.
Examples
Uses collection slicing and filtering, joinToString, nullable processors, erased List checks, herds, comparators, and copyData.
What it assumes
Builds from familiar Java-like generic syntax before introducing Kotlin-specific reification and variance.
What you can do after
Build it from scratch by declaring generic functions and classes, constraining nullability, and selecting variance for producer or consumer APIs.
How it connects
Connects type parameters to nullability, type inference, type erasure, reified type parameters, subtyping, variance, and Java wildcards.

Also explained in

Compare how 20 books explain generics →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Kotlin in Action, 2E. The section links point to the parts of the book it draws on.