Tech

Kotlin

Kotlin is a modern, statically-typed language that runs on the JVM and is Google's preferred language for Android development. It offers null safety, coroutines for asynchronous programming, extension functions, data classes, and seamless Java interoperability. Interviews focus on its concise syntax, coroutine model, sealed classes, scope functions, and how it improves upon Java's verbosity while maintaining full backward compatibility with existing Java libraries and Android APIs.

What you get

Questions

20

Difficulty

3 levels

Answer Formats

2

Use the toggle on each card to move between an interview-ready answer and a simpler explanation. Questions are sorted from beginner to advanced, and the keywords are highlighted. You can also blur the answers to practice recalling them from memory.

Questions

Practice the answers out loud.

All topics

Question 1

What are the main advantages of Kotlin over Java for Android development?

Beginner

How to answer in an interview

Kotlin offers null safety (preventing NullPointerExceptions at compile time), concise syntax (reducing boilerplate by ~40%), coroutine support for asynchronous programming, extension functions, data classes, and sealed classes. It is fully interoperable with Java, so existing Java libraries and Android APIs work seamlessly. Kotlin is now Google's preferred language for Android development, with first-class tooling support in Android Studio, including Kotlin-specific features like code completion, debugging, and refactoring.

Question 2

What is the difference between `val` and `var` in Kotlin?

Beginner

How to answer in an interview

`val` declares a read-only reference (like Java's `final`) — the variable cannot be reassigned after initialization, but the object it references may still be mutable. `var` declares a mutable variable that can be reassigned. Kotlin encourages using `val` by default for immutability and thread safety. For collections, `listOf()` creates immutable lists while `mutableListOf()` creates mutable ones. The distinction between the reference being immutable and the object being mutable is important for understanding Kotlin's mutability model.

Question 3

How does Kotlin handle default and named arguments?

Beginner

How to answer in an interview

Kotlin functions and constructors can have default parameter values, eliminating the need for many overloads. Callers can skip parameters that have defaults. Named arguments let you specify parameters by name, allowing you to skip leading parameters and only pass the ones you need, improving readability. This reduces boilerplate significantly compared to Java, where you need multiple overloaded methods. Default and named arguments work in constructors, regular functions, and extension functions.

Question 4

How do collections work in Kotlin (mutable vs immutable)?

Beginner

How to answer in an interview

Kotlin distinguishes between read-only (`List`, `Map`, `Set`) and mutable (`MutableList`, `MutableMap`, `MutableSet`) collection interfaces. `listOf()` returns an immutable list; `mutableListOf()` returns a mutable one. Read-only collections are not truly immutable at runtime — they are the same JVM classes as mutable ones, but the Kotlin compiler hides mutating methods. The standard library provides rich functional APIs (`map`, `filter`, `groupBy`, `associate`, etc.) that return new collections, encouraging an immutable data style. `toList()`, `toSet()`, and `toMap()` create defensive copies.

Question 5

What are null safety features in Kotlin?

Intermediate

How to answer in an interview

Kotlin distinguishes between nullable (`String?`) and non-nullable (`String`) types at compile time. Safe calls (`?.`) chain operations that may be null without crashing. The Elvis operator (`?:`) provides a default value when a nullable expression is null. The `!!` operator forces a value and throws an NPE if null. The `let` scope function enables safe handling of non-null values. This system eliminates most null-related crashes at compile time rather than runtime, making Kotlin code more reliable than Java code.

Question 6

What are data classes in Kotlin?

Intermediate

How to answer in an interview

Data classes automatically generate `equals()`, `hashCode()`, `toString()`, `copy()`, and `componentN()` functions based on the primary constructor parameters. They are ideal for holding data (DTOs, models, value objects). The `copy()` method creates a modified duplicate without changing the original. Data classes reduce boilerplate significantly compared to Java's equivalent code. They require at least one primary constructor parameter, and the parameters must be `val` or `var`.

Question 7

What are extension functions in Kotlin?

Intermediate

How to answer in an interview

Extension functions add new functionality to existing classes without inheritance. They are defined with a receiver type (e.g., `fun String.isPalindrome(): Boolean`). Inside the function, `this` refers to the receiver instance. Extensions are resolved statically — the compiler determines which extension to call at compile time based on the declared type. They are commonly used for utility functions, DSL builders, and enhancing framework classes. Infix extensions (e.g., `to` in `Pair`) enable cleaner call-site syntax.

Question 8

What are scope functions in Kotlin?

Intermediate

How to answer in an interview

Scope functions (`let`, `run`, `with`, `apply`, `also`) execute a block of code within the context of an object. `let` is used for null checks and transformations; `run` combines object creation and computation; `with` is a non-extension function for operating on an existing object; `apply` configures an object and returns it; `also` performs side effects and returns the object. Choosing the right scope function depends on whether you need the result, the object reference (`it` vs `this`), and the intended purpose (transformation vs configuration vs side effect).

Question 9

How does Kotlin handle functional programming?

Intermediate

How to answer in an interview

Kotlin supports functional programming through higher-order functions (functions that take or return functions), lambdas with concise syntax (`{ x -> x * 2 }` or implicit `it`), immutable collections (by default, with `toList()` creating immutable copies), and standard library functions like `map`, `filter`, `reduce`, `flatMap`, and `fold`. `inline` functions eliminate lambda overhead for higher-order functions. Kotlin encourages immutable data and declarative patterns while remaining fully interoperable with imperative Java code.

Question 10

What is the difference between `lateinit` and `lazy` in Kotlin?

Intermediate

How to answer in an interview

`lateinit` declares a `var` that will be initialized later — it's useful when you can't initialize in the constructor (e.g., dependency injection, `onCreate`). Accessing it before initialization throws `UninitializedPropertyAccessException`. `lazy` is a delegate that initializes a `val` on first access and caches the result — thread-safe by default using `LazyThreadSafetyMode.SYNCHRONIZED`. `lateinit` is for mutable properties you set once externally; `lazy` is for expensive computations you want deferred and immutable.

Question 11

What are companion objects in Kotlin?

Intermediate

How to answer in an interview

Companion objects are Kotlin's replacement for Java's `static` members. Each class can have one companion object, defined inside the class body. Members of the companion object are associated with the class itself, not instances. They can implement interfaces, have properties and functions, and serve as factory methods or hold singleton state. Use `@JvmStatic` to expose companion object members as true statics to Java callers. Unlike Java static methods, companion objects are full objects that can participate in inheritance and be passed as values.

Question 12

How does Kotlin's `when` expression differ from Java's `switch`?

Intermediate

How to answer in an interview

Kotlin's `when` is a powerful replacement for Java's `switch`: it supports any type as a branch condition (not just constants), allows multiple values per branch with commas, supports ranges (`in 1..10`), type checks (`is String`), and arbitrary boolean expressions. When used as an expression (with a return value), `when` must be exhaustive — covering all possible cases or including an `else` branch. Smart casts automatically narrow types inside branches (e.g., after `is String`, the variable is automatically a `String`). Sealed class hierarchies make `when` exhaustive without `else`.

Question 13

What is the `by` keyword used for in Kotlin?

Intermediate

How to answer in an interview

The `by` keyword enables delegation in Kotlin — both property delegation and class delegation. Property delegates use `by` to delegate property access to a separate object: `by lazy` for lazy initialization, `by observable` for change notifications, and `by map` for storing properties in a map. Class delegation (`class Child : Parent by Parent()`) forwards all interface method calls to the specified delegate object, replacing inheritance-based composition. Delegation follows the composition-over-inheritance principle and reduces boilerplate for cross-cutting concerns.

Question 14

What are coroutines in Kotlin?

Advanced

How to answer in an interview

Coroutines are Kotlin's lightweight threading solution for asynchronous programming. Functions marked with `suspend` can pause execution without blocking threads. `launch` starts a fire-and-forget coroutine; `async` returns a `Deferred` result. Structured concurrency ensures coroutines are scoped and cancelled properly (e.g., tied to a ViewModel lifecycle). Dispatchers (`Dispatchers.Main`, `Dispatchers.IO`, `Dispatchers.Default`) control which thread pool runs the coroutine. Coroutines simplify network calls, database operations, and any I/O-bound work compared to callbacks or RxJava.

Question 15

Explain sealed classes and interfaces in Kotlin.

Advanced

How to answer in an interview

Sealed classes (and sealed interfaces, since Kotlin 1.5) define restricted class hierarchies — all subclasses must be known at compile time and declared in the same file (or as nested classes). This enables exhaustive `when` expressions without an `else` branch, ensuring all cases are handled. They are ideal for modeling finite state machines, result types, and UI states (e.g., Loading, Success, Error). Sealed classes provide type safety and compile-time guarantees that are impossible with open class hierarchies.

Question 16

What are Kotlin DSLs and how are they built?

Advanced

How to answer in an interview

Kotlin DSLs (Domain-Specific Languages) are type-safe builders created using lambdas with receivers, extension functions, and scope functions. The receiver type inside a lambda gets `this` as the implicit receiver, enabling clean, nested syntax. The `@DslMarker` annotation prevents accidental access to outer receivers. DSLs are used in Gradle build scripts, Jetpack Compose UI, Exposed database queries, and HTML builders. They provide IDE support (completion, error checking) that string-based DSLs cannot, while remaining readable and declarative.

Question 17

What is the `inline` keyword and when should you use it?

Advanced

How to answer in an interview

The `inline` keyword copies the function body and lambda parameters to the call site, eliminating the overhead of creating lambda objects and method calls for higher-order functions. It's beneficial for small utility functions that take lambdas (e.g., `let`, `run`, `synchronized`). `noinline` prevents specific lambda parameters from being inlined (useful when they need to be stored). `crossinline` prevents non-local returns from within the lambda. Inlining is a performance optimization — use it for frequently called higher-order functions, but avoid it for large functions since it increases code size.

Question 18

What are reified type parameters in Kotlin?

Advanced

How to answer in an interview

Reified type parameters allow you to access the actual type of a generic parameter at runtime — something normally impossible due to JVM type erasure. They only work with `inline` functions. By marking the type parameter `reified` (e.g., `inline fun <reified T> Gson.fromJson(json: String): T`), you can use `is T`, `T::class`, and other type operations inside the function. This eliminates the need to pass `Class<T>` objects explicitly and enables cleaner APIs for JSON parsing, database queries, and intent extras in Android.

Question 19

What are inline classes in Kotlin?

Advanced

How to answer in an interview

Inline classes (now called value classes with `@JvmInline`) wrap a single value to add type safety without runtime overhead. At compile time, the wrapper is replaced by the underlying value — no object allocation occurs. They're useful for preventing primitive obsession (e.g., `UserId` wrapping `String` instead of passing raw strings). Rules: they must have exactly one property in the primary constructor, cannot be null (the underlying type handles nullability), and cannot have backing fields or init blocks. They provide type safety at zero runtime cost.

Question 20

How does Kotlin multiplatform work?

Advanced

How to answer in an interview

Kotlin Multiplatform (KMP) allows sharing code across platforms (Android, iOS, web, desktop) using a shared module. You write common code in `commonMain` and define `expect` declarations for platform-specific APIs. Each platform provides `actual` implementations in `androidMain`, `iosMain`, etc. The compiler generates platform-specific artifacts. KMP shares business logic, networking, data models, and utilities — not UI. Libraries like Ktor, SQLDelight, and Koin have multiplatform support. KMP is complementary to native UI frameworks (Jetpack Compose, SwiftUI) rather than replacing them.

More in Tech

Keep browsing related topics.

Browse all