Advance Kotlin Interview Questions and Answers
Kotlin is a powerful and expressive language that is widely used for Android development, backend systems, and more.
If you’re preparing for an advanced Kotlin interview, mastering key concepts like coroutines, generics, inline functions, and serialization is essential.
In this post, we’ve compiled some of the most important advanced Kotlin interview questions and answers to help you excel in your technical interview.
1. What are coroutines in Kotlin, and how do they work?
Answer: Coroutines are a concurrency design pattern in Kotlin that allow efficient asynchronous programming without blocking threads. They are lightweight because they use suspension instead of blocking.
Example:
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L) // Non-blocking delay
println("Coroutines in Kotlin")
}
println("Hello,")
}
Key Points:
- Uses
suspend
functions to avoid blocking. launch
andasync
are used to create coroutines.runBlocking
is a bridge between coroutines and normal blocking code.
2. Explain the difference between launch
and async
in Kotlin coroutines.
Answer:
launch {}
is used when a coroutine is started that does not return a result.async {}
is used when a coroutine returns a Deferred, which can be awaited usingawait()
.
Example:
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
delay(1000)
println("Using launch")
}
val result = async {
delay(1000)
"Using async"
}
job.join()
println(result.await())
}
3. What are inline functions in Kotlin? How do they help performance?
Answer: Inline functions allow the compiler to replace function calls with the actual function body, reducing memory overhead.
Example:
inline fun printInline(message: () -> Unit) {
println("Before inline")
message()
println("After inline")
}
fun main() {
printInline { println("Inside inline function") }
}
4. Explain reified
type parameters in Kotlin. Why are they useful?
Answer: Normally, generic type parameters are erased at runtime (type erasure
). The reified
keyword in inline functions allows access to the actual type at runtime.
Example:
inline fun <reified T> getTypeName(): String {
return T::class.simpleName ?: "Unknown"
}
fun main() {
println(getTypeName<Int>()) // Output: Int
}
5. What is the difference between List
, MutableList
, and Array
in Kotlin?
Answer:
Feature | List<T> | MutableList<T> | Array<T> |
---|---|---|---|
Mutability | Immutable | Mutable | Mutable |
Size | Fixed | Dynamic | Fixed |
Performance | Fast iteration | Dynamic resizing | Fast access |
Example | listOf(1, 2, 3) | mutableListOf(1, 2, 3) | arrayOf(1, 2, 3) |
6. How does Kotlin support function delegation?
Answer: Kotlin provides by
keyword to delegate function calls.
Example:
interface Printer {
fun printMessage()
}
class ConsolePrinter : Printer {
override fun printMessage() {
println("Printing from ConsolePrinter")
}
}
class PrinterDelegate(p: Printer) : Printer by p
fun main() {
val printer = PrinterDelegate(ConsolePrinter())
printer.printMessage() // Output: Printing from ConsolePrinter
}
7. What is Kotlin’s object
keyword, and where is it used?
Answer: The object
keyword is used for singleton objects, anonymous objects, and companion objects.
Example (Singleton):
object Singleton {
val name = "Kotlin"
fun printName() = println(name)
}
fun main() {
Singleton.printName() // Output: Kotlin
}
8. What are Kotlin DSLs (Domain-Specific Languages)?
Answer: DSLs allow writing expressive and readable APIs using Kotlin’s language features.
Example (Custom DSL):
class Person {
var name: String = ""
fun introduce() = println("Hi, I'm $name")
}
fun person(init: Person.() -> Unit): Person {
val p = Person()
p.init()
return p
}
fun main() {
person {
name = "John"
}.introduce() // Output: Hi, I'm John
}
9. How does Kotlin achieve null safety?
Answer: Kotlin provides built-in null safety using:
- Nullable types (
?
) - Safe calls (
?.
) - Elvis operator (
?:
) - Not-null assertion (
!!
)
Example:
fun main() {
val name: String? = null
println(name?.length ?: "No Name") // Output: No Name
}
10. How can you create an extension function in Kotlin?
Answer: An extension function allows adding functions to existing classes without modifying them.
Example:
fun String.reverseText(): String {
return this.reversed()
}
fun main() {
println("Kotlin".reverseText()) // Output: niltoK
}
11. What is the difference between vararg
and spread operator
?
Answer:
vararg
allows passing a variable number of arguments.- The spread operator (
*
) is used to pass an array as avararg
.
Example:
fun printNumbers(vararg numbers: Int) {
for (num in numbers) println(num)
}
fun main() {
val array = intArrayOf(1, 2, 3)
printNumbers(*array) // Spread operator
}
12. How does lazy
initialization work in Kotlin?
Answer: lazy
is used for lazy-initialized properties, initialized only when accessed.
Example:
val data: String by lazy {
println("Initializing...")
"Hello, Lazy!"
}
fun main() {
println("Before Access")
println(data) // Output: Initializing... Hello, Lazy!
}
13. What are higher-order functions in Kotlin?
Answer: A higher-order function takes another function as a parameter.
Example:
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int { return operation(a, b) } fun main() { val sum = calculate(5, 3) { x, y -> x + y } println(sum) // 8 }
14. What are sealed classes in Kotlin, and how do they differ from enums?
Answer: Sealed classes allow restricting class hierarchies, ensuring all subclasses are known at compile time. Unlike enums, sealed classes can hold different types of data.
Example:
sealed class Result {
data class Success(val data: String) : Result()
data class Failure(val error: String) : Result()
}
fun handle(result: Result) {
when (result) {
is Result.Success -> println("Success: ${result.data}")
is Result.Failure -> println("Error: ${result.error}")
}
}
15. What is the difference between apply
, let
, run
, and also
in Kotlin?
Answer:
Function | Returns | Context Object | Usage |
---|---|---|---|
apply | Object | this | Modify properties |
let | Lambda result | it | Perform operations |
run | Lambda result | this | Execute logic |
also | Object | it | Side effects |
Example:
val person = Person().apply { name = "John" }
person.let { println(it.name) }
16. What is the difference between Lazy
and lateinit
in Kotlin?
Answer:
lateinit
is used for non-nullable properties initialized later.lazy
initializes a val property only when accessed.
Example:
val name: String by lazy { "Kotlin" }
lateinit var description: String
17. How does Kotlin handle checked exceptions?
Answer: Unlike Java, Kotlin does not force checked exceptions, making code cleaner.
Example:
fun readFile() {
throw IOException("File not found")
}
18. How does Kotlin’s type alias work?
Answer: Type aliases provide shorter names for complex types.
Example:
typealias NameMap = Map<String, String>
val names: NameMap = mapOf("Alice" to "Kotlin")
19. What are higher-order functions in Kotlin?
Answer: Functions that take other functions as parameters.
Example:
fun operate(a: Int, b: Int, op: (Int, Int) -> Int): Int = op(a, b)
fun main() {
println(operate(2, 3) { x, y -> x + y })
}
20. What is covariance and contravariance in Kotlin generics?
Answer:
out T
(Covariant): Can be returned but not consumed.in T
(Contravariant): Can be consumed but not returned.
Example:
interface Producer<out T> { fun produce(): T }
interface Consumer<in T> { fun consume(t: T) }
21. What is with
in Kotlin?
Answer: with
is used for performing multiple operations on an object.
Example:
val builder = StringBuilder().apply {
append("Hello, ")
append("World!")
}
println(builder.toString())
22. What is reflection in Kotlin?
Answer: Reflection allows inspecting classes and methods at runtime.
Example:
fun main() {
val kClass = "Hello"::class
println(kClass.simpleName) // Output: String
}
23. How do you implement custom delegates in Kotlin?
Answer: Using operator
functions.
Example:
import kotlin.reflect.KProperty
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String = "Delegated Property"
}
class Example {
val value: String by Delegate()
}
fun main() {
println(Example().value)
}
24. What are tail-recursive functions in Kotlin?
Answer: Tail recursion optimizes recursive calls by reusing the same stack frame.
Example:
tailrec fun factorial(n: Int, result: Int = 1): Int {
return if (n == 1) result else factorial(n - 1, result * n)
}
fun main() {
println(factorial(5)) // Output: 120
}
25. What is operator
overloading in Kotlin?
Answer: Kotlin allows overloading operators like +
, -
, *
, etc.
Example:
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point) = Point(x + other.x, y + other.y)
}
fun main() {
val p1 = Point(2, 3)
val p2 = Point(4, 5)
println(p1 + p2) // Output: Point(x=6, y=8)
}
26. What is inline function in Kotlin, and when should you use it?
Answer: Inline functions reduce function call overhead by copying the function body at the call site.
Example:
inline fun execute(block: () -> Unit) {
println("Before block execution")
block()
println("After block execution")
}
fun main() {
execute { println("Inside block") }
}
27. What are reified types in Kotlin?
Answer: Reified types allow type information to be retained at runtime, useful in inline functions.
Example:
inline fun <reified T> printType() {
println(T::class.simpleName)
}
fun main() {
printType<String>() // Output: String
}
28. How does Kotlin’s object
keyword work?
Answer:
- Used for singleton objects.
- Used in companion objects for static-like behavior.
- Can create anonymous objects.
Example:
object Singleton {
val name = "I am a Singleton"
}
fun main() {
println(Singleton.name)
}
29. What is the difference between companion object
and object
in Kotlin?
Answer:
companion object
provides static-like members inside a class.object
is used to create singletons.
Example:
class MyClass {
companion object {
fun show() = "Companion Object Function"
}
}
fun main() {
println(MyClass.show())
}
30. What is the difference between suspend functions and normal functions in Kotlin?
Answer:
suspend
functions are used in coroutines to support asynchronous execution.- Normal functions block the thread.
Example:
suspend fun fetchData() {
delay(1000) // Suspends execution without blocking thread
println("Data Fetched")
}
31. What is flow
in Kotlin?
Answer: Flow
is a cold stream used to emit multiple values over time.
Example:
fun simpleFlow(): Flow<Int> = flow {
for (i in 1..3) {
delay(1000)
emit(i)
}
}
fun main() = runBlocking {
simpleFlow().collect { println(it) }
}
32. What are channels in Kotlin Coroutines?
Answer: Channels provide a way to send and receive values between coroutines.
Example:
val channel = Channel<Int>()
fun main() = runBlocking {
launch {
for (x in 1..5) channel.send(x)
channel.close()
}
for (y in channel) println(y)
}
33. What is StateFlow
and SharedFlow
in Kotlin?
Answer:
StateFlow
: Holds a single latest value and emits updates.SharedFlow
: Allows multiple emissions likeLiveData
.
Example:
val stateFlow = MutableStateFlow(0)
fun main() {
stateFlow.value = 10
println(stateFlow.value) // Output: 10
}
34. What is coroutineScope
vs. supervisorScope
in Kotlin?
Answer:
coroutineScope
: Cancels all child coroutines if one fails.supervisorScope
: Allows child coroutines to fail independently.
Example:
fun main() = runBlocking {
supervisorScope {
launch { throw RuntimeException("Failure") }
launch { delay(1000); println("Still running") }
}
}
35. What is the difference between launch
and async
in Kotlin Coroutines?
Answer:
launch
returnsJob
and does not return a result.async
returnsDeferred
and allows returning a result.
Example:
fun main() = runBlocking {
val job = launch { delay(1000); println("Launch done") }
val result = async { delay(1000); "Async Result" }
job.join()
println(result.await())
}
36. What is the difference between Dispatchers.IO
, Dispatchers.Default
, and Dispatchers.Main
?
Answer:
Dispatchers.IO
: Used for I/O operations like file and network operations.Dispatchers.Default
: Used for CPU-intensive tasks.Dispatchers.Main
: Used for UI-related operations.
Example:
fun main() = runBlocking {
launch(Dispatchers.IO) { println("IO Dispatcher") }
launch(Dispatchers.Default) { println("Default Dispatcher") }
launch(Dispatchers.Main) { println("Main Dispatcher") }
}
37. What is yield()
in Kotlin Coroutines?
Answer: yield()
allows a coroutine to release CPU resources and resume later without blocking.
Example:
fun main() = runBlocking {
launch {
println("Before Yield")
yield()
println("After Yield")
}
}
38. What is Mutex
in Kotlin, and how is it different from synchronized
?
Answer:
Mutex
is used to synchronize coroutines without blocking threads.- Unlike
synchronized
, it does not block threads but suspends coroutines instead.
Example:
val mutex = Mutex()
suspend fun safeFunction() {
mutex.withLock {
println("Critical section")
}
}
39. What are the advantages of Kotlin Serialization over Gson?
Answer:
- Faster performance
- Safer null handling
- Supports Kotlin-specific features
Example:
@Serializable
data class User(val name: String, val age: Int)
val json = Json.encodeToString(User("John", 30))
println(json) // Output: {"name":"John","age":30}
40. What is the difference between map
, flatMap
, and fold
in Kotlin?
Answer:
map
: Transforms each element and returns a list.flatMap
: Flattens and transforms elements.fold
: Accumulates a value over elements.
Example:
val list = listOf(1, 2, 3)
println(list.map { it * 2 }) // [2, 4, 6]
println(list.flatMap { listOf(it, it * 2) }) // [1, 2, 2, 4, 3, 6]
println(list.fold(0) { acc, i -> acc + i }) // 6
41. What is Nothing
type in Kotlin?
Answer: Nothing
represents a function that never returns (e.g., infinite loops or exceptions).
Example:
fun fail(): Nothing {
throw IllegalArgumentException("Error")
}
42. How does takeIf
and takeUnless
work in Kotlin?
Answer:
takeIf
: Returns the object if condition is true, otherwise null.takeUnless
: Returns the object if condition is false, otherwise null.
Example:
val number = 5.takeIf { it > 3 }
println(number) // Output: 5
43. What is a type projection
in Kotlin generics?
Answer: Restricts how a generic type can be used with in
and out
keywords.
Example:
fun copy(from: Array<out Number>, to: Array<Number>) {
from.forEachIndexed { i, value -> to[i] = value }
}
44. How does lazy
delegation work in Kotlin?
Answer: lazy
initializes a property only when accessed.
Example:
val name: String by lazy { "Kotlin" }
45. What is a builder pattern
in Kotlin?
Answer: A pattern for step-by-step object creation.
Example:
class PersonBuilder {
var name = ""
fun build() = Person(name)
}
val person = PersonBuilder().apply { name = "John" }.build()
46. What is inline class
in Kotlin?
Answer: A class that wraps a value but avoids memory overhead.
Example:
@JvmInline
value class UserId(val id: String)
47. What is opt-in
feature in Kotlin?
Answer: Allows using experimental APIs explicitly.
Example:
@OptIn(ExperimentalStdlibApi::class)
fun test() {}
48. How does Kotlin handle method overloading?
Answer: Supports overloading based on parameter count and type.
Example:
fun greet() = "Hello"
fun greet(name: String) = "Hello, $name"
49. What is inline property
in Kotlin?
Answer: Used to define properties that are optimized at runtime.
Example:
inline val String.lengthSquared get() = this.length * this.length
50. What are Kotlin contracts?
Answer: Contracts help the compiler optimize smart casting.
Example:
fun String?.isValid(): Boolean {
contract { returns(true) implies (this@isValid != null) }
return this != null
}
Conclusion
These advanced Kotlin interview questions cover a wide range of topics, including coroutines, functional programming, serialization, and more.
By understanding these concepts and practicing with real-world examples, you can confidently tackle any Kotlin interview.
Keep coding and stay updated with the latest Kotlin features!