skip to content

Generic Declarations

How you write generic functions, classes, and interfaces in Kotlin, and how you constrain their type parameters with upper bounds and where-clauses. Interviewers use this as the setup before the harder variance and erasure questions.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

explore

questions

20

How do you declare a generic class in Kotlin, such as a simple Box<T> that holds a value of type T, and how do you create an instance of it?

level: juniorimportance: must knowfreq 70%

answer

  1. Type param in <> after the class name
  2. No `new` — call the constructor like a function
  3. Inference fills in T from the argument
  4. T usable in properties, params, return types
  5. Erasure: can't do T() or is T

basics

~10 s

Put a type parameter in angle brackets after the class name: class Box<T>(val value: T). Then create it like Box(42) or Box<String>("hi"); Kotlin usually infers the type for you.

solid answer

~40 s

Declare the type parameter in angle brackets right after the class name: `class Box<T>(val value: T)`. `T` is a placeholder that becomes a concrete type per instance. You reference `T` in the constructor, properties, and methods. Construct with `Box(42)` — the compiler infers `T = Int` from the argument — or state it explicitly: `Box<String>("hi")`. Unlike Java, Kotlin has no `new` keyword. A common gotcha: you cannot write `class Box<T>(val value: T = T())` to default-construct a `T`, because `T` is erased at runtime and the compiler has no constructor for an unknown type. Type parameters are conventionally single uppercase letters (`T`, `E`, `K`, `V`) but can be any valid identifier.

code

kotlin · 10 lines
kotlin
class Box<T>(val value: T) {
    fun get(): T = value
}

fun main() {
    val ints = Box(42)             // Box<Int>
    val text = Box<String>("hi")  // explicit
    println(ints.get())            // 42
    println(text.get())            // hi
}

go deeper

for a junior

Knows the class Box<T>(...) syntax, that there's no new, and that the type is usually inferred.

for a middle

Explains where T can appear (properties, params, returns) and that all uses share one concrete type per instance.

for a senior

Brings up erasure: why T()/is T don't work and how a factory or value argument fixes it.

for a principal

Frames API design trade-offs — when to make a class generic vs. accept Any, and how erasure shapes the public surface.

## What a generic class is A **generic class** is a class parameterized by one or more **type parameters** — placeholders for a type that the caller fills in. This lets one class work safely with many element types without casting or `Any`. ## Syntax The type parameter list goes in angle brackets **immediately after the class name**: ```kotlin class Box<T>(val value: T) { fun get(): T = value fun isSame(other: T): Boolean = value == other } ``` - `T` is the **type parameter** (a name; `T` is just convention). - Inside the class body you may use `T` as a property type, parameter type, or return type. - The angle-bracket form `<T>` is the **declaration**; at the use site `Box<Int>` is a **type argument**. ## Creating instances — no `new` Kotlin has no `new` keyword. You call the constructor like a function: ```kotlin val a = Box(42) // T inferred as Int val b = Box<String>("hi") // T given explicitly val c: Box<Long> = Box(7L) // inferred from context ``` Type **inference** fills in `T` from the constructor argument or the expected type, so explicit `<...>` is usually optional. ## Referencing T across members `T` can appear anywhere a type is expected inside the class — in `val`/`var` properties, function parameters, and return types. All uses of `T` in one instance refer to the **same** concrete type. ## A key limitation: erasure Kotlin uses **type erasure** (inherited from the JVM): at runtime `Box<Int>` and `Box<String>` are the same raw `Box`. Therefore you **cannot** do `T()` to construct a `T`, call `T::class`, or use `is T` inside an ordinary class — there is no runtime type info for `T`. You must pass a value or a factory in instead. ## Conventional names `T` (type), `E` (element), `K`/`V` (key/value), `R` (result). Any identifier is legal, but stick to convention for readability.

  • Why can't you write `class Box<T>(val value: T = T())`?
    Type parameters are erased at runtime, so the compiler has no constructor for the unknown type `T`. You must pass a value or a factory function instead.
  • Does Kotlin need the `new` keyword to instantiate a generic class?
    No. Kotlin has no `new`; you call the constructor like a regular function, e.g. `Box(42)`.

A generic class is a labeled shipping box: the same box design carries anything, and the label (type argument) records what's inside so you can't pull out the wrong thing.

saying these in an interview costs you the question

  • Putting the type parameter after the constructor parentheses instead of after the class name
  • Using `new Box<Int>(...)` (Java syntax) in Kotlin
  • Claiming you can default a property with `T()` inside the class
  • Thinking each property could have a different concrete T within one instance

context

open as a page

How do you declare a generic function in Kotlin, and where does the type parameter go relative to the function name?

level: juniorimportance: must knowfreq 80%

basics

~10 s

You write the type name in angle brackets right before the function name, like fun <T> first(list: List<T>): T. T is a placeholder for any type the caller uses.

open as a page

What is the default upper bound of a Kotlin type parameter like <T> when you don't specify one, and why does this matter?

level: juniorimportance: must knowfreq 70%

basics

~10 s

If you write <T> with no bound, T can be any type including a nullable one. The hidden default is Any?, so values of T may be null.

open as a page

In Kotlin generics, what does the `where` clause do, and how does it differ from writing an upper bound inline like `<T : CharSequence>`?

level: juniorimportance: must knowfreq 55%

basics

~20 s

The where clause lets a type parameter require more than one constraint at once. Inline syntax like <T : CharSequence> can only state one bound, so when you need two or more you move them into where.

open as a page

How do you declare a class or interface with multiple type parameters, like Map<K, V>, and reference those parameters across the members?

level: middleimportance: must knowfreq 60%

basics

~10 s

List the parameters comma-separated in angle brackets: class Pair<K, V>(val key: K, val value: V). Each name is its own placeholder, and you can use both anywhere a type is needed inside the class.

open as a page

How does Kotlin infer the type argument of a generic function at a call site, and when does inference fail?

level: middleimportance: must knowfreq 70%

basics

~20 s

Kotlin guesses T from the values you pass in or from how the result is used, so you usually don't write the type yourself. It fails when there's nothing for it to read the type from.

open as a page

How do you write a generic function that refuses nullable type arguments, and what exactly does <T : Any> guarantee at compile time?

level: middleimportance: must knowfreq 60%

basics

~10 s

Add the bound <T : Any>. Then callers cannot use a nullable type like String? for T, so the value is guaranteed non-null and you can use it without null checks.

open as a page

Write a generic function whose type parameter must be both a `CharSequence` and `Comparable<T>`. Show exactly where the `where` clause goes in a function and in a class declaration.

level: middleimportance: must knowfreq 45%

basics

~10 s

Declare the parameter as plain <T>, then after the return type (function) or the supertypes (class) add where T : CharSequence, T : Comparable<T> before the body.

open as a page

How can a class implement a generic interface, and what choices do you have for the interface's type parameter when you do?

level: middleimportance: should knowfreq 45%

basics

~10 s

When a class implements a generic interface, it either fixes the type argument to a concrete type (class IntBox : Container<Int>) or stays generic and forwards its own parameter (class Box<T> : Container<T>).

open as a page

When and how do you pass explicit type arguments to a generic function, e.g. maxOf<Int>(...)? Why might you do it even when inference would work?

level: middleimportance: should knowfreq 55%

basics

~10 s

You write the type in angle brackets right after the function name, like maxOf<Int>(a, b). You do it when Kotlin can't figure the type out, or to force a specific type or improve clarity.

open as a page

How does the upper bound of a type parameter affect which members and operations you can call on a value of that type inside the function body?

level: middleimportance: should knowfreq 40%

basics

~10 s

Inside the function, a value of type T only exposes the members of its upper bound. With no bound (Any?) you get almost nothing; with <T : Number> you get Number's methods like toInt().

open as a page

Inside a function with `where T : CharSequence, T : Comparable<T>`, which members of `T` can you call, and why does combining bounds widen the available API?

level: middleimportance: should knowfreq 38%

basics

~20 s

You can use every member from all the bounds at once. With CharSequence plus Comparable<T> you get length, indexing, and compareTo/the comparison operators on the same value, because T is effectively the intersection of the bounds.

open as a page

When referencing a type parameter T across a generic class's members, what is the default upper bound of an unbounded T, and how do star projections (Box<*>) relate to instances of a generic class?

level: seniorimportance: should knowfreq 40%

basics

~20 s

An unbounded T defaults to the nullable top type Any?, so T can be null unless you constrain it. Box<*> is a star projection — a Box of some unknown type you can read from safely but can't pass specific values into.

open as a page

The standard library declares Comparable<in T> and Iterable<out T>. What do the `in` and `out` modifiers on a generic interface's type parameter mean, and why are they placed where they are?

level: seniorimportance: should knowfreq 55%

basics

~10 s

out means T is only produced (returned), so the interface is covariant — Iterable<Cat> is an Iterable<Animal>. in means T is only consumed (passed in), so it's contravariant — Comparable<Animal> is a Comparable<Cat>.

open as a page

At runtime, what happens to a generic function's type parameter T, and how does that limit what you can do with T inside the body?

level: seniorimportance: should knowfreq 50%

basics

~20 s

By default the type T is erased — it disappears at runtime — so inside the function you can't check or create T directly (no x is T, no T()). The type only exists at compile time.

open as a page

Why is the bound written as <T : Comparable<T>> in functions like a generic max(), and what does this self-referential bound enforce?

level: seniorimportance: should knowfreq 45%

basics

~10 s

<T : Comparable<T>> means T must be comparable to its own type. It lets you call a.compareTo(b) on two T values, which is what max/min need to order them.

open as a page

A teammate writes `fun <T> mid(a: T, b: T) where T : Number, T : Comparable<T>` and calls `mid(1, 2)`. It compiles, but `mid(1.0, 2)` does not. Explain why, and what the multi-bound `where` actually requires of the argument types.

level: seniorimportance: should knowfreq 30%

basics

~20 s

T must be one single type that is both a Number and Comparable to itself. mid(1, 2) infers T = Int, which qualifies. mid(1.0, 2) mixes Double and Int, so no single T satisfies both arguments and the bounds, and inference fails.

open as a page

You need a function that returns the single element of a collection or throws. Would you write a generic function, and how does using a type parameter compare to writing per-type overloads or returning Any?

level: seniorimportance: nice to knowfreq 35%

basics

~10 s

Yes, write one generic function fun <T> single(c: Collection<T>): T. It works for every element type, returns the exact type, and avoids copy-pasting a version per type or returning Any and forcing casts.

open as a page

When designing a public generic API, how do you decide between leaving a type parameter unbounded (<T>), bounding to <T : Any>, or bounding to a specific type, and what are the compatibility consequences of changing it later?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Pick the loosest bound that still lets your code work. Use <T> when null is fine, <T : Any> to ban null, a specific bound when you need that type's methods. Tightening a bound later can break callers.

open as a page

When designing a public API, how would you decide between a multi-bound `where` clause (e.g. `where T : Persistable, T : Auditable`) versus introducing a single combined interface? What are the trade-offs?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Use a multi-bound where when callers' types already implement the separate interfaces and you don't want to force them to add a new marker. Introduce a combined interface only when the combination is a real, reusable domain concept worth naming.

open as a page