Toolmingo
Guides23 min read

50 Kotlin Interview Questions (With Answers)

Top Kotlin interview questions with clear answers and code examples — covering null safety, coroutines, data classes, sealed classes, extension functions, and Android development.

Kotlin interviews test your understanding of null safety, coroutines, functional idioms, object-oriented design, and Android-specific patterns. This guide covers 50 common questions — with concise answers and code examples.

Quick reference

Topic Most asked questions
Basics val/var, types, string templates, null safety
Functions Default args, extension functions, lambdas, infix
Classes Data classes, sealed classes, object/companion, delegation
Coroutines suspend, CoroutineScope, launch/async, Flow
Collections map/filter/reduce, sequences, destructuring
Android ViewModel, StateFlow, Compose, Room
Advanced Generics, reified, inline, DSLs

Basics

1. What is the difference between val and var?

val name = "Alice"   // read-only (immutable reference)
var count = 0        // mutable
count = 1            // OK
name = "Bob"         // compile error

val is equivalent to Java's final. The underlying object can still be mutable (e.g., a val list can have items added if it's a MutableList).


2. What is Kotlin's type system and how does it handle null?

Kotlin distinguishes between nullable and non-nullable types at the type level:

var name: String = "Alice"   // non-nullable — cannot be null
var nick: String? = null     // nullable — can be null

name.length         // safe
nick.length         // compile error — must handle null
nick?.length        // safe call — returns null if nick is null
nick!!.length       // non-null assertion — throws NPE if null
nick?.length ?: 0   // Elvis operator — default value if null
Operator Meaning
?. Safe call — returns null if receiver is null
?: Elvis — provides default when left side is null
!! Non-null assertion — throws NullPointerException
as? Safe cast — returns null on failure

3. What are string templates in Kotlin?

val name = "Alice"
val age = 30

println("Hello, $name!")                    // simple variable
println("She is ${age + 1} next year")      // expression
println("Name length: ${name.length}")      // property access

Multi-line strings use triple quotes and trimIndent():

val text = """
    Hello, $name.
    Age: $age.
""".trimIndent()

4. What is the difference between == and === in Kotlin?

val a = "hello"
val b = "hello"
val c = a

a == b    // true — structural equality (calls equals())
a === b   // true in this case (string pool), but not guaranteed
a === c   // true — same reference

val x = Integer(1)
val y = Integer(1)
x == y    // true
x === y   // false — different objects
Operator Java equivalent Meaning
== .equals() Structural (value) equality
=== == Referential equality

5. What are Kotlin's basic types?

Category Types
Integer Byte, Short, Int, Long
Floating Float, Double
Character Char
Boolean Boolean
String String
Arrays Array<T>, IntArray, LongArray, etc.

Kotlin has no primitive types in the source code — the compiler maps them to JVM primitives automatically. Use UInt, ULong, etc. for unsigned types.


Functions

6. What are default and named parameters?

fun greet(name: String, greeting: String = "Hello") {
    println("$greeting, $name!")
}

greet("Alice")                    // Hello, Alice!
greet("Bob", greeting = "Hi")    // Hi, Bob!
greet(greeting = "Hey", name = "Carol")  // named — order doesn't matter

Named parameters make calls self-documenting and eliminate overload chains.


7. What are extension functions?

Extension functions add behaviour to existing classes without inheriting or modifying them:

fun String.isPalindrome(): Boolean {
    val clean = this.lowercase().filter { it.isLetterOrDigit() }
    return clean == clean.reversed()
}

"racecar".isPalindrome()   // true
"hello".isPalindrome()     // false

They are resolved statically (at compile time), not dynamically. They cannot override member functions.

// Useful standard library examples
listOf(1, 2, 3).sumOf { it * 2 }   // 12
"hello world".split(" ")            // [hello, world]
42.coerceIn(1, 100)                 // 42

8. What is the difference between a lambda and an anonymous function?

// Lambda
val multiply = { x: Int, y: Int -> x * y }

// Anonymous function — can use return to exit itself
val divide = fun(x: Int, y: Int): Int {
    if (y == 0) return 0   // returns from anonymous function
    return x / y
}

In a lambda, return returns from the enclosing function (non-local return). Use anonymous functions or return@label for local returns:

listOf(1, 2, 3).forEach { n ->
    if (n == 2) return@forEach   // continues to next iteration
    println(n)
}

9. What are higher-order functions?

Functions that take functions as parameters or return functions:

fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
    val result = mutableListOf<T>()
    for (item in this) if (predicate(item)) result.add(item)
    return result
}

val evens = listOf(1, 2, 3, 4).filter { it % 2 == 0 }  // [2, 4]

Common Kotlin standard library higher-order functions:

Function Purpose
map Transform each element
filter Keep elements matching predicate
reduce / fold Accumulate to single value
flatMap Map then flatten
groupBy Group into map by key
sortedBy Sort by selector
let, run, apply, also, with Scope functions

10. What are Kotlin's scope functions?

Function Context object Return value Use case
let it Lambda result Null checks, transforms
run this Lambda result Configuration + compute result
apply this Context object Object configuration
also it Context object Side effects (logging)
with this Lambda result Group calls on an object
val user = User().apply {
    name = "Alice"
    age = 30
}

val result = user.run {
    "$name is $age years old"
}

user.let { u ->
    println(u.name)
}

Classes

11. What is a data class?

data class Point(val x: Int, val y: Int)

val p1 = Point(1, 2)
val p2 = Point(1, 2)

p1 == p2          // true — structural equality
p1.toString()     // Point(x=1, y=2)
val p3 = p1.copy(y = 5)   // Point(x=1, y=5)
val (x, y) = p1   // destructuring

The compiler generates equals(), hashCode(), toString(), copy(), and componentN() functions automatically. Data classes must have at least one val/var primary constructor parameter.


12. What are sealed classes and when do you use them?

Sealed classes restrict the class hierarchy to a fixed set of subclasses, all defined in the same file:

sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val message: String) : Result<Nothing>()
    object Loading : Result<Nothing>()
}

fun handle(result: Result<User>) = when (result) {
    is Result.Success -> showUser(result.data)
    is Result.Error   -> showError(result.message)
    Result.Loading    -> showSpinner()
    // No else needed — compiler knows all subclasses
}

When to use: representing a finite set of states (network results, UI states, parser outputs).


13. What is the difference between object, companion object, and class?

// object — singleton, created lazily on first access
object Config {
    val timeout = 5000
}

// companion object — static-like members on a class
class User(val name: String) {
    companion object {
        fun create(name: String) = User(name)  // factory
    }
}
val user = User.create("Alice")

// class — regular class, instantiated with constructor
class Counter {
    var count = 0
}
Concept Instance Purpose
object Single (singleton) Utility, singletons
companion object One per class Factory methods, constants
class Multiple General use

14. What are interfaces in Kotlin? How do they differ from abstract classes?

interface Drawable {
    fun draw()                          // abstract
    fun describe() = "I am drawable"   // default implementation
}

abstract class Shape {
    abstract fun area(): Double
    fun name() = "Shape"               // concrete method
}

class Circle(val r: Double) : Shape(), Drawable {
    override fun area() = Math.PI * r * r
    override fun draw() = println("Drawing circle")
}
Feature Interface Abstract class
Multiple inheritance Yes (multiple interfaces) No (single class)
Constructor No Yes
State (fields) Only abstract/val with backing Yes
Default methods Yes Yes

15. What is class delegation with by?

interface Logger {
    fun log(msg: String)
}

class ConsoleLogger : Logger {
    override fun log(msg: String) = println(msg)
}

// Delegates all Logger methods to the provided instance
class Service(logger: Logger) : Logger by logger {
    fun doWork() {
        log("Working...")  // delegated to logger
    }
}

val service = Service(ConsoleLogger())
service.doWork()  // Working...

The by keyword implements the Delegation pattern automatically — no boilerplate forwarding methods needed.


16. What are open, final, and abstract modifiers?

In Kotlin, all classes and functions are final by default (unlike Java):

open class Base {
    open fun greet() = "Hello"    // can be overridden
    fun fixed() = "Cannot override"
}

class Child : Base() {
    override fun greet() = "Hi"   // OK
    // override fun fixed() = ...  // compile error
}

abstract class Shape {
    abstract fun area(): Double   // must be overridden
}

17. What is a data object (Kotlin 1.9+)?

data object Singleton {
    val value = 42
}

println(Singleton)          // Singleton (not Singleton@1a2b3c)
Singleton == Singleton      // true

Like object but generates toString() and meaningful equals()/hashCode() — useful for sealed class singletons.


Coroutines

18. What is a coroutine?

A coroutine is a suspendable computation — it can pause execution (at suspend points) without blocking a thread, then resume later (potentially on a different thread).

suspend fun fetchUser(id: Int): User {
    delay(1000)       // suspends — releases the thread
    return api.getUser(id)
}

Benefits over threads:

  • Lightweight (thousands of coroutines on a few threads)
  • Structured concurrency (scoped lifetime)
  • Sequential code style for async operations
  • Built-in cancellation

19. What is the difference between launch and async?

// launch — fire-and-forget, returns Job
val job = CoroutineScope(Dispatchers.IO).launch {
    doWork()
}
job.cancel()   // can cancel

// async — returns Deferred<T>, use await() to get result
val deferred = CoroutineScope(Dispatchers.IO).async {
    fetchData()
}
val result = deferred.await()   // suspends until done
launch async
Returns Job Deferred<T>
Result None await() returns T
Exception Propagates to scope Held until await()
Use case Side effects Parallel computation

20. What are Kotlin coroutine Dispatchers?

Dispatcher Thread pool Use case
Dispatchers.Main Main thread UI updates (Android)
Dispatchers.IO Elastic pool (64+) Network, disk I/O
Dispatchers.Default CPU cores CPU-intensive work
Dispatchers.Unconfined Caller's thread Testing, edge cases
withContext(Dispatchers.IO) {
    val data = api.fetch()           // runs on IO thread
    withContext(Dispatchers.Main) {
        updateUI(data)               // switches to main thread
    }
}

21. What is structured concurrency?

Coroutines are launched inside a CoroutineScope. When the scope is cancelled, all child coroutines are cancelled:

// viewModelScope cancels when ViewModel is destroyed
viewModelScope.launch {
    val users = async { fetchUsers() }
    val orders = async { fetchOrders() }
    show(users.await(), orders.await())   // parallel
}

Rules:

  1. A parent waits for all children before completing
  2. Cancelling a parent cancels all children
  3. A child's exception propagates to the parent

22. What is Flow in Kotlin?

Flow is a cold, asynchronous data stream — it only produces values when collected:

fun numbers(): Flow<Int> = flow {
    for (i in 1..5) {
        delay(100)
        emit(i)
    }
}

// Collect
viewModelScope.launch {
    numbers()
        .filter { it % 2 == 0 }
        .map { it * it }
        .collect { println(it) }   // 4, 16
}
Flow StateFlow SharedFlow
Cold/Hot Cold Hot Hot
Replay None Last value Configurable
Use case One-shot streams UI state Events

23. What is the difference between StateFlow and LiveData?

Feature StateFlow LiveData
Framework Kotlin Coroutines Android Jetpack
Lifecycle aware Manual (repeatOnLifecycle) Built-in
Initial value Required Optional
Thread safety Any thread, UI update via collect postValue for background
Compose support Native Needs .observeAsState()
Nullability Non-null Nullable
// ViewModel
private val _uiState = MutableStateFlow(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()

// Fragment
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.uiState.collect { state -> render(state) }
    }
}

24. How do you handle exceptions in coroutines?

// SupervisorJob — sibling failures don't cancel each other
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

scope.launch {
    try {
        val result = fetchData()
    } catch (e: IOException) {
        showError(e.message)
    }
}

// CoroutineExceptionHandler for unhandled exceptions
val handler = CoroutineExceptionHandler { _, exception ->
    Log.e("App", "Caught: $exception")
}
CoroutineScope(handler + Dispatchers.IO).launch {
    throw RuntimeException("Oops")
}

Note: CoroutineExceptionHandler only catches exceptions in launch, not async (those surface at await()).


Collections

25. What is the difference between List and MutableList?

val immutable: List<Int> = listOf(1, 2, 3)
val mutable: MutableList<Int> = mutableListOf(1, 2, 3)

mutable.add(4)       // OK
// immutable.add(4)  // compile error

// Kotlin collection interfaces
// List ← MutableList
// Set ← MutableSet
// Map ← MutableMap

Read-only views: listOf, setOf, mapOf. Mutable: mutableListOf, etc. For performance-critical code use ArrayList, HashMap directly.


26. What are sequences and when do you use them?

// Eager evaluation — creates intermediate lists
listOf(1..1_000_000)
    .map { it * 2 }
    .filter { it % 3 == 0 }
    .take(10)
    .toList()

// Lazy evaluation — no intermediate lists
(1..1_000_000).asSequence()
    .map { it * 2 }
    .filter { it % 3 == 0 }
    .take(10)
    .toList()   // only processes what's needed

Use sequences when:

  • The collection is large
  • You chain multiple operations
  • You use take(n) / first() (early termination)

Use regular collections when the list is small — sequence overhead isn't worth it.


27. What is destructuring in Kotlin?

// Data class
data class Point(val x: Int, val y: Int)
val (x, y) = Point(3, 4)

// List / Array
val (first, second, *rest) = listOf(1, 2, 3, 4, 5)  // spread not supported in destructuring
val list = listOf(1, 2, 3)
val (a, b, c) = list   // OK for exactly 3 elements

// Map entry
for ((key, value) in mapOf("a" to 1, "b" to 2)) {
    println("$key -> $value")
}

// Pair / Triple
val pair = "Alice" to 30
val (name, age) = pair

Works via componentN() operator functions generated by data class or defined manually.


Android-specific

28. What is a ViewModel and why use it in Android?

class UserViewModel(private val repo: UserRepository) : ViewModel() {
    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users.asStateFlow()

    init { loadUsers() }

    private fun loadUsers() {
        viewModelScope.launch {
            _users.value = repo.getUsers()
        }
    }
}

ViewModel:

  • Survives configuration changes (screen rotation)
  • Separates UI state from UI logic
  • Scoped to Activity/Fragment lifecycle
  • Use viewModelScope for coroutines — auto-cancelled on onCleared()

29. What is Jetpack Compose and how does state work?

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }

    Column {
        Text("Count: $count")
        Button(onClick = { count++ }) {
            Text("Increment")
        }
    }
}
Property wrapper Purpose
remember Survives recomposition, lost on config change
rememberSaveable Survives config change (saves to Bundle)
mutableStateOf Observable state — triggers recomposition
collectAsStateWithLifecycle Collects StateFlow safely

State should flow down (via parameters), events flow up (via callbacks) — "state hoisting" pattern.


30. What is Room and how does it work with coroutines?

@Entity
data class User(
    @PrimaryKey val id: Int,
    val name: String
)

@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun getAll(): Flow<List<User>>   // reactive — emits on DB change

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(user: User)

    @Delete
    suspend fun delete(user: User)
}

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

Room generates SQL at compile time, validates queries, and integrates with coroutines (suspend functions) and Flow.


Advanced

31. What are generics and type variance in Kotlin?

// Invariant — only exact type
class Box<T>(val value: T)

// Covariant (out) — producer, T can be supertype
class ReadOnly<out T>(val value: T)
fun printAnimal(box: ReadOnly<Animal>) { ... }
val cat: ReadOnly<Cat> = ReadOnly(Cat())
printAnimal(cat)   // OK — Cat is a subtype of Animal

// Contravariant (in) — consumer, T can be subtype
class Writer<in T> {
    fun write(item: T) { ... }
}
val animalWriter: Writer<Animal> = Writer()
val catWriter: Writer<Cat> = animalWriter   // OK
Variance Keyword Can read Can write Use case
Covariant out Yes No Producers (Source)
Contravariant in No Yes Consumers (Sink)
Invariant none Yes Yes Read + write

32. What is reified and inline?

// Without reified — T is erased at runtime
fun <T> List<Any>.filterIsInstance(): List<T> { /* T not available */ }

// With inline + reified — T is available at runtime
inline fun <reified T> List<Any>.filterIsInstance(): List<T> {
    return this.filter { it is T }.map { it as T }
}

val list: List<Any> = listOf(1, "hello", 2, "world")
val strings: List<String> = list.filterIsInstance<String>()   // [hello, world]

inline copies the function body at the call site. reified makes the type parameter available at runtime (bypassing JVM type erasure). Only works with inline functions.


33. What is a Kotlin DSL?

Kotlin's extension functions + lambdas with receivers enable clean DSL syntax:

// HTML DSL example
html {
    head { title { +"Page Title" } }
    body {
        h1 { +"Hello, World!" }
        p { +"This is Kotlin DSL" }
    }
}

// Gradle Kotlin DSL
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
    testImplementation(kotlin("test"))
}

Common Kotlin DSLs: Ktor routing, Kotlin HTML, Gradle build scripts, Exposed ORM, Anko.


34. What is delegation with by lazy?

class HeavyObject {
    val expensive: String by lazy {
        println("Computing...")
        "expensive result"
    }
}

val obj = HeavyObject()
println(obj.expensive)   // Computing... → expensive result
println(obj.expensive)   // expensive result (cached)

by lazy initializes on first access and caches the result. Thread-safe by default (LazyThreadSafetyMode.SYNCHRONIZED). For single-thread use: by lazy(LazyThreadSafetyMode.NONE).


35. What are operator functions?

data class Vector(val x: Int, val y: Int) {
    operator fun plus(other: Vector) = Vector(x + other.x, y + other.y)
    operator fun times(scale: Int) = Vector(x * scale, y * scale)
    operator fun component1() = x
    operator fun component2() = y
}

val v1 = Vector(1, 2)
val v2 = Vector(3, 4)
val v3 = v1 + v2            // Vector(4, 6)
val v4 = v1 * 3             // Vector(3, 6)
val (x, y) = v3             // destructuring via component1/2

Common operator functions: plus, minus, times, div, rem, unaryMinus, inc, dec, get, set, contains, invoke.


Kotlin vs Java

36. What does Kotlin eliminate from Java?

Java pain point Kotlin solution
NullPointerException Nullable types (?), Elvis, safe call
Checked exceptions No checked exceptions
Verbose getters/setters Properties with backing field
Boilerplate POJOs data class
Static methods companion object, top-level functions
Anonymous classes Lambda expressions
instanceof + cast Smart cast
Builders Named parameters + defaults

37. How does Kotlin interop with Java?

// Calling Java from Kotlin — seamless
val list: java.util.ArrayList<String> = ArrayList()
list.add("hello")

// Platform types — Java types without null annotation
val javaString: String! = JavaClass.getString()   // could be null

// Annotations for Java interop
class MyClass {
    @JvmStatic fun staticMethod() = "static"
    @JvmField val field = 42
    @JvmOverloads fun greet(name: String, greeting: String = "Hello") = "$greeting, $name"
}
Annotation Purpose
@JvmStatic Generates static method on companion object
@JvmField Exposes property as public field (no getter/setter)
@JvmOverloads Generates overloaded methods for default params
@Throws Declares checked exceptions for Java callers

Testing

38. How do you write unit tests in Kotlin?

// JUnit 5 + MockK
class UserServiceTest {
    private val repo = mockk<UserRepository>()
    private val service = UserService(repo)

    @Test
    fun `getUser returns user when found`() {
        every { repo.findById(1) } returns User(1, "Alice")

        val result = service.getUser(1)

        assertEquals("Alice", result.name)
        verify { repo.findById(1) }
    }

    @Test
    fun `getUser throws when not found`() {
        every { repo.findById(99) } returns null

        assertThrows<UserNotFoundException> {
            service.getUser(99)
        }
    }
}

39. How do you test coroutines?

@Test
fun `fetchData returns result`() = runTest {
    val repo = mockk<DataRepository>()
    coEvery { repo.fetchData() } returns "data"

    val viewModel = MyViewModel(repo)
    viewModel.load()

    assertEquals("data", viewModel.state.value.data)
}

// Testing Flow
@Test
fun `flow emits correct values`() = runTest {
    val flow = flowOf(1, 2, 3)
    val results = flow.toList()
    assertEquals(listOf(1, 2, 3), results)
}

Use runTest from kotlinx-coroutines-test — it replaces delay with virtual time and runs synchronously.


Common scenarios

40. How do you parse JSON with Kotlin?

// kotlinx.serialization (recommended)
@Serializable
data class User(val id: Int, val name: String)

val json = Json { ignoreUnknownKeys = true }
val user = json.decodeFromString<User>("{\"id\":1,\"name\":\"Alice\"}")
val string = json.encodeToString(user)

// Gson
val gson = Gson()
val user = gson.fromJson(jsonString, User::class.java)

// Moshi
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
val adapter = moshi.adapter(User::class.java)
val user = adapter.fromJson(jsonString)

41. How do you make network requests in Kotlin (Android)?

// Retrofit + coroutines
interface ApiService {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: Int): User
}

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()

val service = retrofit.create(ApiService::class.java)

viewModelScope.launch {
    try {
        val user = service.getUser(1)
        _uiState.value = UiState.Success(user)
    } catch (e: HttpException) {
        _uiState.value = UiState.Error(e.message())
    }
}

42. How do you implement dependency injection in Kotlin?

// Hilt (Android) — most common
@HiltAndroidApp
class MyApp : Application()

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides @Singleton
    fun provideRetrofit(): Retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .build()
}

@HiltViewModel
class UserViewModel @Inject constructor(
    private val repo: UserRepository
) : ViewModel()

@AndroidEntryPoint
class MainActivity : AppCompatActivity()

Alternatives: Koin (simpler, no code generation) and Dagger 2 (Hilt is built on Dagger).


Enums & when

43. What are Kotlin enums?

enum class Direction(val degrees: Int) {
    NORTH(0), EAST(90), SOUTH(180), WEST(270);

    fun opposite() = when (this) {
        NORTH -> SOUTH
        SOUTH -> NORTH
        EAST  -> WEST
        WEST  -> EAST
    }
}

Direction.NORTH.degrees      // 0
Direction.NORTH.opposite()   // SOUTH
Direction.values()           // array of all
Direction.valueOf("EAST")    // Direction.EAST

44. What is the when expression?

// Replaces switch — always exhaustive with sealed/enum
val result = when (x) {
    is String -> "String of length ${x.length}"
    is Int    -> "Int: $x"
    null      -> "null"
    else      -> "Unknown"
}

// No argument — acts like if-else chain
val grade = when {
    score >= 90 -> "A"
    score >= 80 -> "B"
    score >= 70 -> "C"
    else        -> "F"
}

// Multiple conditions on same branch
when (day) {
    "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> "Weekday"
    "Saturday", "Sunday" -> "Weekend"
}

Error handling

45. How does Kotlin handle exceptions?

// All exceptions are unchecked
fun readFile(path: String): String {
    return try {
        File(path).readText()
    } catch (e: FileNotFoundException) {
        ""
    } catch (e: IOException) {
        throw RuntimeException("Failed to read $path", e)
    } finally {
        println("Done")
    }
}

// Result type — functional error handling
fun divide(a: Int, b: Int): Result<Int> = runCatching {
    if (b == 0) throw ArithmeticException("Division by zero")
    a / b
}

divide(10, 2).onSuccess { println(it) }
             .onFailure { println(it.message) }

46. What is the runCatching function?

val result = runCatching {
    apiCall()
}

// Fold — handle both cases
result.fold(
    onSuccess = { data -> showData(data) },
    onFailure = { error -> showError(error) }
)

// Map — transform the success value
val transformed = result
    .map { it.toUpperCase() }
    .getOrDefault("fallback")

// recover — handle failure with fallback
val safe = result.recover { e ->
    when (e) {
        is NetworkException -> "offline"
        else -> throw e
    }
}

Miscellaneous

47. What are type aliases?

typealias UserId = Int
typealias UserMap = Map<UserId, String>
typealias Callback<T> = (Result<T>) -> Unit

fun fetchUser(id: UserId, callback: Callback<User>) { ... }

// Makes code more readable, no runtime overhead

48. What is const val vs val?

const val MAX_SIZE = 100         // compile-time constant
val runtimeVal = computeSize()   // runtime constant

// const val restrictions:
// - Must be top-level or in companion object / object
// - Must be String or primitive type
// - Value known at compile time

object Config {
    const val TIMEOUT = 5000
    val name = "App"   // val — initialized at runtime
}

49. What is @JvmInline value class?

@JvmInline
value class UserId(val id: Int)

fun findUser(id: UserId): User? { ... }
findUser(UserId(42))   // type-safe, no boxing at runtime

// vs plain Int — no type confusion
fun badFind(id: Int) { ... }
badFind(42)   // could accidentally pass any Int

Value classes wrap a single value with type safety. At runtime, they are typically unboxed (inlined), giving zero overhead compared to the raw type.


50. What are the most common Kotlin anti-patterns?

Anti-pattern Problem Fix
Overusing !! Runtime NPE Use ?., ?:, requireNotNull
Mutable shared state with coroutines Race conditions Use StateFlow, Channel, or Mutex
Java-style Kotlin (static utils class) Not idiomatic Top-level functions, extension functions
Blocking in coroutine (Thread.sleep) Blocks thread Use delay()
GlobalScope.launch Lifecycle leaks Use viewModelScope, lifecycleScope
runBlocking in production Android ANR on main thread Use viewModelScope.launch
Ignoring structured concurrency Memory leaks Let scope manage coroutine lifetime
Data classes with mutable properties (var) Accidental mutation Prefer val in data classes

Comparison table

Feature Kotlin Java Swift
Null safety Type system (?) Annotations only Optional type
Coroutines Built-in CompletableFuture async/await
Data classes data class Records (Java 16+) struct
Extension functions Yes No Yes
Type inference Strong Limited Strong
Sealed classes Yes Sealed classes (17+) Enum with associated values
Functional style First-class Streams API Combine framework
Android Primary language Legacy No
Multi-platform Kotlin Multiplatform No No

Common mistakes

Mistake Consequence Fix
val list = mutableListOf(...) leaked as mutable Unexpected mutations Expose as List<T> with toList()
delay() outside coroutine Compile error Mark function suspend
Using == on arrays Compares references Use contentEquals()
object for stateful singletons Hard to test Use DI instead
Not using coEvery in MockK tests suspend not mocked Always use coEvery/coVerify
copy() on deeply nested data class Shallow copy Use manual deep copy or @Serializable
Forgetting viewLifecycleOwner in Fragment Lifecycle leak Use viewLifecycleOwner.lifecycleScope
Catching Exception in coroutines catching CancellationException Breaks cancellation Catch specific exceptions or rethrow CancellationException

FAQ

Is Kotlin replacing Java on Android? Yes — Google declared Kotlin the preferred language for Android in 2019. New Android APIs (Jetpack Compose, KSP) are Kotlin-first. Most new Android projects use Kotlin exclusively.

Can Kotlin run on iOS? Via Kotlin Multiplatform (KMP) you can share business logic (repositories, ViewModels, networking) between Android, iOS, web, and desktop. The iOS UI is still SwiftUI/UIKit. KMP is production-ready and used by Netflix, McDonald's, and VMware.

Is Kotlin slower than Java? At runtime — no. Kotlin compiles to the same JVM bytecode as Java. Compile times can be slightly longer due to features like coroutines and inline functions. Kotlin Native (non-JVM) may have different performance characteristics.

Do I need to learn Java before Kotlin? Not strictly. Kotlin is a good first JVM language. However, many Android libraries and legacy codebases are in Java, so Java reading ability is useful.

What is the difference between Kotlin and Kotlin Multiplatform? Standard Kotlin targets the JVM (and Android). KMP adds compilation targets: iOS (via Kotlin/Native), browser (via Kotlin/JS), desktop (native or JVM). The shared commonMain code is regular Kotlin.

What are suspend functions not allowed to do? You cannot call a suspend function from non-coroutine code without runBlocking (which blocks the calling thread). Suspend functions cannot be @JvmStatic. They also cannot be called from Java code directly without a coroutine wrapper.

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools