Kotlin Interview Questions — Practice Quiz

Reviewed by Mark Dickie · Last updated

7 questions on this page you can answer and see marked on the spot. Go to the first one

Kotlin is a statically typed programming language that runs on the JVM and compiles to bytecode, JavaScript, or native binaries. For interview preparation you should know null safety (?, !!, safe-call chains), coroutine basics (suspend functions, CoroutineScope, structured concurrency), and the difference between val/var/const val. Expect questions on sealed classes, data classes, extension functions, value classes (@JvmInline value class), and how Kotlin's type system differs from Java's — especially around generics variance and reified type parameters.

What topics does a Kotlin interview typically cover?

TopicKey ConceptsCommon Question Types
Null safety?., ?:, !!, platform typesExplain safe-call vs non-null assertion; debug NPE scenarios
Coroutinessuspend, launch, async, DispatcherConvert callback code to coroutines; reason about cancellation
Data & sealed classescopy(), when exhaustiveness, destructuringModel a state machine with sealed classes
Genericsvariance (in/out), reified, star-projectionDeclare a covariant container; explain why Array<Number> can't hold Array<Int>
Extension functionsreceiver type, scope, resolutionWrite an extension on List<Int>; discuss static dispatch

How should you prepare for Kotlin coding rounds?

  1. Write small coroutine examples by hand — interviewers like asking you to spot a cancellation or threading bug on paper.
  2. Know the standard library collections API (map, filter, fold, groupBy, partition) cold; you will use it in live coding.
  3. Understand when as an expression and how exhaustiveness checking works with sealed hierarchies.
  4. Be able to explain by delegates (lazy, observable, vetoable) and when to write a custom delegate.
  5. Review JVM interop: @JvmStatic, @JvmOverloads, @JvmField, and platform-type nullability pitfalls.

The quiz below tests these areas with real questions pulled from interview reports. Take it to find your weak spots before the real thing.

Key facts

  • Tarmac has 114 Kotlin interview questions on this topic, 10 of them on this page, at difficulty 1–5 of 5.
  • Tarmac tracked 165 job postings asking for Kotlin in September 2026.
  • Roles asking for Kotlin advertise a median base salary of US$169,050, across 44 job postings as of September 2026.
  • Tarmac last reviewed these Kotlin interview questions on 20 October 2026.

At a glance

Questions10 shown · 114 in the bank
Difficulty1–5 of 5
FormatsTrue / false, Multiple choice, Ordering, Fill in the blank, Find the bug, Short answer, Multiple answer, Code output, Flashcard

What you'll review

  1. inline value classes
  2. data classes
  3. object declarations
  4. sealed classes

Practice questions

Try one before you open the answer. Pick an option and press Check; it's marked on the spot.

In Kotlin, a value class (declared with value class on the JVM) must have exactly one read-only property in its primary constructor.#

Options

Show answer

True. A Kotlin value class must declare exactly one read-only (val) property in its primary constructor; the compiler inlines this single underlying value wherever possible and rejects zero- or multi-property definitions at compile time.

Why:

The Kotlin language spec requires every value class to wrap exactly one underlying value, declared as a val property in the primary constructor. A value class with zero properties or with more than one property will not compile.

When a Kotlin value class instance is stored in a generic collection such as List<MyValueClass>, the instance is boxed (i.e., an actual wrapper object is created) rather than being represented purely by its underlying value.#

Options

Show answer

True. Storing a Kotlin value class instance in a generic collection like List<MyValueClass> forces boxing because generics use erased reference types at runtime; the compiler cannot inline the underlying value where the static type is not concrete.

Why:

Inlining can only happen when the compiler knows the concrete static type. Generic collections erase type information and store references, so the value class must be boxed into an object at runtime. This is one of the well-known cases where boxing occurs despite the class being declared value.

Which functions does the Kotlin compiler automatically generate for a data class based on the properties declared in its primary constructor?#

Options

Show answer

Kotlin's data class modifier causes the compiler to auto-generate equals(), hashCode(), toString(), copy(), and componentN() functions for the properties declared in the primary constructor. The componentN() functions enable destructuring declarations, and copy() creates a shallow copy with optional property overrides.

Why:

For each property in the primary constructor, the Kotlin compiler generates equals()/hashCode() based on those properties, toString() returning the class name and property values, copy() for creating a shallow copy with optional overrides, and componentN() functions to support destructuring declarations.

Place the following events in the order they occur when a Kotlin object declaration is first accessed at runtime.#

Put these in order

Show answer

When a Kotlin object is first accessed, its class loads and the static initializer fires, then the superclass constructor runs, then property initializers and init blocks execute in top-to-bottom textual order, and finally the fully constructed singleton instance is returned to the caller. Kotlin objects are lazily initialized on first use.

Why:

Kotlin object declarations are initialized lazily — the singleton is not created until first access (the object's class is loaded and its static initializer is triggered on first access). When initialization begins, the superclass constructor runs first (the declared superclass constructor (if any) executes), mirroring normal class construction. Then property initializers and init blocks run in textual order (property initializers and init blocks execute in textual (top-to-bottom) order) — so x is initialized, then the init block runs, then y. Only after all initialization completes is the fully constructed instance handed back to the caller (the fully initialized singleton instance is returned to the caller).

In Kotlin 2.x, what keyword is used (together with class) to declare a value class? Fill in the blank:#

Show answer

In Kotlin 2.x, what keyword is used (together with class) to declare a value class? Fill in the blank:

value class UserId(val id: String)

Why:

As of Kotlin 2.0, the inline modifier was removed from value class declarations. A value class is now declared with just value class, e.g. value class UserId(val id: String). The compiler still replaces usages with the underlying type at compile time where possible, but the inline keyword is no longer part of the syntax.

The following Kotlin code fails to compile. What is the bug?#

sealed class Operation

data class Add(val a: Int, val b: Int) : Operation()
data class Subtract(val a: Int, val b: Int) : Operation()
data class Multiply(val a: Int, val b: Int) : Operation()

fun execute(op: Operation): Int = when (op) {
    is Add -> op.a + op.b
    is Subtract -> op.a - op.b
}

Options

Show answer

The when expression does not cover the Multiply subclass, so it is non-exhaustive and the code will not compile.

Why:

The when expression is used as the return value of execute, making it an expression form that Kotlin requires to be exhaustive. Operation is a sealed class with three direct subclasses: Add, Subtract, and Multiply. The when only handles Add and Subtract, omitting Multiply. Because sealed classes enable exhaustiveness checking, the compiler rejects the non-exhaustive when. Adding an is Multiply -> op.a * op.b branch (or an else branch) would fix the compile error.

In Kotlin, what single constraint must the primary constructor of a value class satisfy regarding its properties? Specifically, how many properties can it declare, what kind (val/var), and where must they appear?#

Show answer

A value class's primary constructor must declare exactly one read-only (val) property, and that property must be declared in the primary constructor parameter list itself (not in the class body). It cannot declare zero properties, more than one property, or a var property.

Why:

The Kotlin specification requires that a value class has exactly one underlying property, which must be a val declared in the primary constructor header. var properties, multiple properties, or properties declared only in the class body are all disallowed. The single val becomes the representation that the compiler inlines.

Consider the following Kotlin data class and select every statement that is TRUE about the functions the compiler auto-generates for it.#

Options

Pick every one that applies.

Show answer

For a Kotlin data class, the compiler generates componentN(), equals(), hashCode(), toString(), and copy() using only the primary constructor properties. Body-declared properties are excluded from all of these. Additionally, data classes cannot be marked open — they are implicitly final.

Why:

The compiler generates componentN(), equals(), hashCode(), toString(), and copy() based ONLY on the primary constructor parameters. Properties declared in the class body (like greeting) are excluded from all auto-generated functions. Therefore the statement that the auto-generated componentN() functions are produced only for properties declared in the primary constructor, not for properties declared in the class body is true — componentN() is generated only for name and age. The statement that the auto-generated copy() function has parameters matching only the primary constructor properties is true — copy() has parameters for name and age only. The claim that the auto-generated equals() and hashCode() consider every property in the class, including those declared in the body outside the primary constructor is false because greeting is NOT included in equals()/hashCode(). The claim that a data class can be declared open so that other classes may inherit from it is false because data classes cannot be open (they are final by default and the compiler rejects open data class). The claim that the auto-generated toString() includes body-declared properties in addition to primary constructor properties is false because toString() only includes primary constructor properties, so greeting does not appear.

What is the exact output of this Kotlin program?#

object A {
    init { println("A init") }
}

object B {
    init { println("B init") }
    val ref = A
}

object C {
    init { println("C init") }
    val ref = B
}

fun main() {
    println("Start")
    C.ref
    println("End")
}

Options

Show answer
Start / C init / B init / A init / End
Why:

Object declarations in Kotlin are initialized lazily on first access — not when the file is loaded. main prints "Start" first. The expression C.ref is the first access to C, so C's initialization begins. Inside an object, initializers and init blocks run top-to-bottom: C's init block prints "C init", then val ref = B is evaluated, which triggers B's initialization. B's init block prints "B init", then val ref = A triggers A's initialization, printing "A init". With all three objects initialized, C.ref completes and main prints "End". The result is a depth-first, lazy chain: C → B → A.

In Kotlin, an @JvmInline value class wrapping a single underlying field is erased at the bytecode level to that field's type when possible, but boxing into the generated wrapper still happens in several well-defined situations. List the concrete scenarios (categories of scenario) in which the Kotlin compiler is forced to box an inline value class instance, and for each explain why erasure to the bare underlying type is insufficient.#

Show answer

An inline value class is boxed whenever the compiler cannot statically guarantee the unerased underlying type suffices. The well-documented cases are: (1) the value class is used as a nullable type (e.g., UserId?) — null cannot be represented by a bare primitive, so the boxed wrapper is required; (2) the value class appears as a type argument to a generic — generics are erased to Object on the JVM, so the wrapper is the only valid representation; (3) it is stored as an element of an array of value-class type — arrays are reified and covariant, so the same erasure-to-Object argument applies; and (4) the value class is used as an interface type that it implements (e.g., assigned to a variable of that interface type or passed where the interface is expected) — interface dispatch goes through a reference type, so the instance must be boxed. In all other cases — local variables, direct method parameters/returns where the static type is the value class itself, and non-nullable assignments — the compiler erases to the underlying type and no allocation occurs.

Why:

This question tests staff-level understanding of Kotlin inline value class boxing semantics. Boxing is forced by the JVM type system and generics erasure, not by arbitrary compiler choices. Nullable usage requires the wrapper because primitives cannot be null. Generic type arguments erase to Object, so the boxed form is the only valid representation. Arrays of value classes face the same erasure-to-Object constraint since JVM arrays are reified. Interface dispatch requires a reference type because the value class, when used as an interface it implements, must be accessed through a reference-typed receiver. Note that cross-module boundaries do NOT force boxing: the Kotlin compiler uses name mangling precisely to preserve value-class identity without requiring the boxed form, and inline lambdas do not inherently box captured value-class parameters — the inline modifier inlines the lambda body at the call site, so capture-based boxing only applies to non-inline escaping closures, which is a general closure-capture concern rather than a value-class-specific forced-boxing rule.

Related interview questions

Job market

See kotlin salaries and hiring demand from live job postings.

The other 104 questions

This page shows 10 and marks what you pick. That's as far as a page can go. A free account opens the other 104 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.

Start with this topic

Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan

Interview booked? Practise the job ad instead

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.