Generic Functions and Classes

Generics look like TypeScript's: type parameters in angle brackets, usually inferred. An upper bound (T : Comparable<T>) restricts T, a where clause lists several bounds, and an unbounded T defaults to Any?, so it may be nullable.

A generic class, a bounded generic function and a reified filterKotlin
class Shelf<T>(private val items: List<T>) {
  fun first(): T? = items.firstOrNull()
}
fun <T : Comparable<T>> List<T>.best(): T? = maxOrNull()
inline fun <reified T> List<Any>.only(): List<T> = filter { it is T }.map { it as T }
fun main() {
  println(Shelf(listOf("The Quiet Harbor")).first()?.uppercase())
  println(listOf(4.6, 4.3, 4.8, 4.1).best())
  println(listOf<Any>("Salt and Saffron", 312, 14.99).only<String>())
}
Output
THE QUIET HARBOR
4.8
[Salt and Saffron]

Type arguments are erased at runtime, so it is T fails in an ordinary generic function, and reified alone does not help:

Two generic functions that do not compileKotlin
fun <T> List<Any>.erased() = filter { it is T }
fun <reified T> List<Any>.notInline() = filter { it is T }
Output
generr.kt:1:45: error: cannot check for instance of erased type 'T (of fun <T>
  List<Any>.erased)'.
generr.kt:2:6: error: only type parameters of inline functions can be reified.

An inline function is copied into each call site (Functions and Lambdas), where the real type is known and can replace T. The standard library's filterIsInstance<T>() works this way, and so do Android's by viewModels<BookViewModel>() and kotlinx.serialization 5,947 's Json.decodeFromString<Book>(text).