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 pageshowhide
explore
- How Kotlin Does Types5 questions
- Special Types & the Type Lattice30 questions
- Any & Any? at the Top5 questions
- Unit — the No-Value Type5 questions
- Nothing — the Bottom Type5 questions
- Nullable Type Unification5 questions
- Type Checks: is / !is5 questions
- Casts: as / as?5 questions
- Generic Declarations20 questions
- Generic Functions5 questions
- Generic Classes & Interfaces5 questions
- Upper Bounds5 questions
- where-Clause Multiple Constraints5 questions
- Variance & Projections20 questions
- Declaration-Site Variance (in / out)5 questions
- Use-Site Projections5 questions
- Star-Projection5 questions
- Variance vs Java Wildcards & PECS5 questions
- Reification & Erasure20 questions
- JVM Type Erasure5 questions
- Reified Type Parameters5 questions
- The inline + reified Pattern5 questions
- Erasure Workarounds & Limits5 questions
- Type Aliases15 questions
- typealias Declarations5 questions
- Function-Type & Generic Aliases5 questions
- typealias vs Inline value class5 questions
questions
110 · 6 sectionsKotlin'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?
basics
~20 sAny 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.
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??
basics
~20 sEvery 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.
Junior-friendly but commonly confused: distinguish Unit, Nothing, and Any in Kotlin's type model, and where each one shows up.
basics
~20 sAny 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.
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?
basics
~20 sVariance 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.
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?
basics
~20 sNormally 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.
In Kotlin, what is `Any`, and how does it relate to `Any?`? Which one is the true root of the whole type system?
basics
~20 sAny 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?.
What is the difference between the as and as? cast operators in Kotlin, and what happens when each fails?
basics
~20 sas 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.
What do the `is` and `!is` operators do in Kotlin, and what is a smart cast?
basics
~20 sis 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.
What is kotlin.Nothing, and how does it differ from Unit?
basics
~10 sNothing 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.
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?
basics
~20 sT 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.
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?
basics
~10 sPut 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.
How do you declare a generic function in Kotlin, and where does the type parameter go relative to the function name?
basics
~10 sYou 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.
What is the default upper bound of a Kotlin type parameter like <T> when you don't specify one, and why does this matter?
basics
~10 sIf 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.
In Kotlin generics, what does the `where` clause do, and how does it differ from writing an upper bound inline like `<T : CharSequence>`?
basics
~20 sThe 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.
How do you declare a class or interface with multiple type parameters, like Map<K, V>, and reference those parameters across the members?
basics
~10 sList 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.
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?
basics
~10 sout 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.
What does the star projection `List<*>` mean in Kotlin, and what can you safely do with such a value?
basics
~20 sList<*> 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.
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?
basics
~10 sArray<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.
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?
basics
~10 sout 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.
What does the `in` modifier mean on a type parameter such as `Comparable<in T>`, and how does subtyping behave for an `in` type?
basics
~10 sin 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.
What is JVM type erasure, and what happens to the type argument of a `List<String>` at runtime in Kotlin?
basics
~10 sAt 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.
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>?
basics
~20 sYou 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.
Why must a function be marked `inline` for its type parameter to be `reified`? What does the compiler do?
basics
~20 sBecause 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.
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?
basics
~20 sMarking 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.
Why does `if (value is List<String>)` fail to compile in Kotlin, while `if (value is List<*>)` is allowed?
basics
~20 sAt 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.
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?
basics
~10 sIt 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.
What is a `typealias` in Kotlin, and what does declaring `typealias Name = ExistingType` actually create?
basics
~20 sA 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.
What is the difference between a `typealias` and a `@JvmInline value class` in Kotlin, and when would you choose each?
basics
~20 sA 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.
How do you declare a parameterized (generic) function-type alias such as a Predicate, and what is its relationship to the underlying function type?
basics
~10 sAdd 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.
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.
basics
~20 sNo. 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.