skip to content

Type System

Kotlin's static type structure: the special types at the top and bottom of the lattice, generic declarations and bounds, declaration-site variance, what erasure takes away and reified gives back, and type aliases. Interviewers use this area to separate people who write Kotlin from people who understand it.

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

explore

questions

110 · 6 sections

Kotlin's type system is often described as a single lattice with a top type and a bottom type. What are those two types, and what does each one mean in practice?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Any is the top type: every non-null value is an Any, so it is the common parent of all types. Nothing is the bottom type: it has no values and means code never returns normally there.

open as a page

Kotlin 'bakes nullability into the type system' rather than treating null as a runtime accident. What does that mean concretely, and how does the type T differ from T??

level: middleimportance: must knowfreq 80%
basics
~20 s

Every type comes in two forms: T can never hold null, T? can. The compiler tracks which one you have and refuses to call methods on a T? until you prove it is not null, so most NullPointerExceptions are caught at compile time.

open as a page

Junior-friendly but commonly confused: distinguish Unit, Nothing, and Any in Kotlin's type model, and where each one shows up.

level: juniorimportance: should knowfreq 60%
basics
~20 s

Any is the parent of all types. Unit is the type for functions that return nothing useful (like void) and has exactly one value. Nothing means a function never returns at all, such as one that always throws.

open as a page

Kotlin emphasizes declaration-site variance with out and in. At the 'how Kotlin models types' level, what problem does this solve and how does it differ from Java's approach?

level: seniorimportance: should knowfreq 55%
basics
~20 s

Variance decides when a List of a subtype counts as a List of a supertype. Kotlin lets the library author declare this once on the type parameter (out for producers, in for consumers), so every user gets it automatically instead of repeating wildcards at each use.

open as a page

Kotlin advertises 'reified generics' for inline functions, even though the JVM erases generics. At the model level, what does reified give you and what are its boundaries?

level: seniorimportance: should knowfreq 50%
basics
~20 s

Normally a generic type is erased, so you cannot check it at runtime. Marking an inline function's type parameter reified makes the actual type available inside the function, so you can write checks like 'is T' without passing a Class object.

open as a page

In Kotlin, what is `Any`, and how does it relate to `Any?`? Which one is the true root of the whole type system?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Any is the parent of every non-null type. Any? adds the possibility of null, so it sits above Any and is the top of the whole type system — every value of any type is an Any?.

open as a page

What is the difference between the as and as? cast operators in Kotlin, and what happens when each fails?

level: juniorimportance: must knowfreq 75%
basics
~20 s

as forces a value to a type and crashes with an error if it is not that type. as? tries the same but gives back null instead of crashing when the value does not match.

open as a page

What do the `is` and `!is` operators do in Kotlin, and what is a smart cast?

level: juniorimportance: must knowfreq 85%
basics
~20 s

is checks at runtime whether a value is of a given type; !is is the negation. After a successful is check, Kotlin automatically treats the variable as that type, so you can use it without an extra cast. That automatic narrowing is a smart cast.

open as a page

What is kotlin.Nothing, and how does it differ from Unit?

level: juniorimportance: must knowfreq 70%
basics
~10 s

Nothing is a type with no values at all; it marks code that never returns normally, like throwing an exception. Unit means a function returns but gives back nothing useful, like void.

open as a page

In Kotlin's type system, what is the relationship between a type T and its nullable counterpart T?, and why does assigning a String to a String? compile without any conversion?

level: juniorimportance: must knowfreq 70%
basics
~20 s

T is a subtype of T?. Every non-null String is also a valid String?, so a String fits anywhere a String? is expected, just like a child fits where a parent is asked for. No conversion happens.

open as a page

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%
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.

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

In Kotlin, what does the `out` modifier on a type parameter mean, as in `interface Source<out T>`, and how does it affect assignment between generic types?

level: juniorimportance: must knowfreq 70%
basics
~10 s

out makes the type parameter covariant: the type only produces (returns) values of T, never consumes them. So a Source<Cat> can be used where a Source<Animal> is expected, because every Cat is an Animal.

open as a page

What does the star projection `List<*>` mean in Kotlin, and what can you safely do with such a value?

level: juniorimportance: must knowfreq 70%
basics
~20 s

List<*> means a list of some unknown element type. You can read items as Any? and call type-agnostic members like size, but you cannot safely add a specific element because the real type is unknown.

open as a page

What is a use-site projection in Kotlin, and why do you need Array<out T> when copying from a source array even though Array is invariant?

level: juniorimportance: must knowfreq 55%
basics
~10 s

Array<T> is invariant, so an Array<Int> is not an Array<Any>. Writing Array<out T> at a parameter says 'I only read from this array', which lets you pass arrays of subtypes safely.

open as a page

How do Kotlin's `out` and `in` variance modifiers map onto Java's `? extends` and `? super` wildcards, and how does this relate to the PECS rule?

level: juniorimportance: must knowfreq 70%
basics
~10 s

out means a type only produces values, like Java's ? extends (a producer). in means a type only consumes values, like Java's ? super (a consumer). PECS: Producer Extends, Consumer Super.

open as a page

What does the `in` modifier mean on a type parameter such as `Comparable<in T>`, and how does subtyping behave for an `in` type?

level: middleimportance: must knowfreq 60%
basics
~10 s

in makes the type parameter contravariant: the type only consumes (accepts) values of T, never returns them. Subtyping is reversed — a Comparable<Animal> can be used where a Comparable<Cat> is expected.

open as a page

What is JVM type erasure, and what happens to the type argument of a `List<String>` at runtime in Kotlin?

level: juniorimportance: must knowfreq 70%
basics
~10 s

At compile time the type knows it holds Strings, but at runtime the JVM forgets the generic part. A List<String> and a List<Int> are just a plain List to the running program.

open as a page

The JVM erases generics at runtime, so a method that takes a List<T> cannot 'see' what T is. How do you pass the concrete type into a non-inline function so it survives to runtime, and what is the difference between Class<T> and KClass<T>?

level: juniorimportance: must knowfreq 60%
basics
~20 s

You hand the type in by hand as a parameter, like a label. Add a Class<T> or KClass<T> argument so the function knows the real type at runtime. Class is the Java type token; KClass is the Kotlin one.

open as a page

Why must a function be marked `inline` for its type parameter to be `reified`? What does the compiler do?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Because inline copies the function's body into each call site. There the compiler already knows the real type you used, so it can replace the type parameter with that concrete class. A normal function has no copy and no known type.

open as a page

What does it mean to declare a function with a `reified` type parameter in Kotlin, and what does it let you do that a normal generic function cannot?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Marking a generic type with reified (on an inline function) keeps the real type available at runtime. So you can write value is T or T::class, which you normally cannot do because generics are erased.

open as a page

Why does `if (value is List<String>)` fail to compile in Kotlin, while `if (value is List<*>)` is allowed?

level: middleimportance: must knowfreq 62%
basics
~20 s

At runtime the program can't tell what's inside a list, only that it's a list. So checking is List<String> is impossible and Kotlin rejects it; is List<*> (a list of anything) is checkable, so it's allowed.

open as a page

What is a function-type alias in Kotlin, and how would you declare an alias named Handler for a function that takes an Event and returns Unit?

level: juniorimportance: must knowfreq 55%
basics
~10 s

It gives a short, readable name to a function type. You write typealias Handler = (Event) -> Unit. Then you can use Handler anywhere instead of writing the long (Event) -> Unit signature.

open as a page

What is a `typealias` in Kotlin, and what does declaring `typealias Name = ExistingType` actually create?

level: juniorimportance: must knowfreq 55%
basics
~20 s

A typealias gives an existing type a second, shorter name. It does not make a new type — it is just an alias the compiler swaps back to the original. The two names are fully interchangeable.

open as a page

What is the difference between a `typealias` and a `@JvmInline value class` in Kotlin, and when would you choose each?

level: juniorimportance: must knowfreq 70%
basics
~20 s

A typealias is just a nickname for an existing type, so it is interchangeable with that type. A value class is a brand-new wrapper type that the compiler keeps separate, so you cannot mix it up with the underlying type.

open as a page

How do you declare a parameterized (generic) function-type alias such as a Predicate, and what is its relationship to the underlying function type?

level: middleimportance: must knowfreq 45%
basics
~10 s

Add type parameters in angle brackets: typealias Predicate<T> = (T) -> Boolean. Then Predicate<String> means (String) -> Boolean. The compiler just substitutes the type argument and expands the alias.

open as a page

A teammate adds `typealias UserId = String` and `typealias OrderId = String` hoping to prevent mixing them up. Will the compiler catch passing an OrderId where a UserId is expected? Explain why.

level: middleimportance: must knowfreq 50%
basics
~20 s

No. Both aliases are just other names for String, so the compiler sees them as the same type and lets you swap them freely. A typealias does not create a new type, so it cannot stop the mix-up.

open as a page