Introduction
This list covers a wide range of Kotlin topics, from basic syntax and data classes to coroutines and advanced language features. Each question includes a code snippet and an explanation of why and how the feature works. Use these examples to solidify your understanding and confidence in Kotlin.
1. What is the primary difference between `val` and `var`?
val name = "John" // Read-only reference
var age = 30 // Mutable reference
Why: `val` creates an immutable reference, while `var` creates a mutable one.
How: Use `val` when you don't plan to reassign the variable. `var` allows reassignment.
2. How do you create a function in Kotlin?
fun greet(name: String): String {
return "Hello, $name"
}
Why: Functions are fundamental building blocks in Kotlin.
How: Use `fun` keyword, provide parameters in parentheses, and specify the return type after `:` if needed.
3. What are nullable types and how do you handle them?
var nullableName: String? = null
println(nullableName?.length) // Safe call
println(nullableName ?: "Unknown") // Elvis operator
Why: Null safety avoids NullPointerExceptions.
How: Use `?` to allow null, `?.` (safe calls) to handle null safely, and the Elvis operator `?:` for defaults.
4. What is a data class?
data class User(val name: String, val age: Int)
val user = User("Alice", 25)
println(user) // toString, equals, hashCode auto-generated
Why: Data classes provide boilerplate-free classes for holding data.
How: Use `data` keyword. Kotlin generates `toString`, `equals`, `hashCode`, and `copy` automatically.
5. How do extension functions work?
fun String.lastChar(): Char = this[this.length - 1]
println("Hello".lastChar()) // 'o'
Why: Extension functions add functionality to existing classes without inheritance.
How: Define `fun ClassName.methodName()` and refer to `this` inside the function body.
6. Explain higher-order functions and lambdas.
fun applyOperation(x: Int, operation: (Int) -> Int): Int {
return operation(x)
}
val result = applyOperation(5) { it * 2 } // passing lambda
println(result) // 10
Why: Higher-order functions and lambdas enable functional programming patterns.
How: A higher-order function takes functions as parameters or returns them. Lambdas are concise function literals.
7. What are default and named arguments?
fun printInfo(name: String = "Guest", age: Int = 0) {
println("$name is $age years old")
}
printInfo() // "Guest is 0 years old"
printInfo(age = 30, name = "Bob") // "Bob is 30 years old"
Why: They improve readability and reduce overloads.
How: Specify default values in the function signature and use named arguments to clarify parameter meaning.
8. Explain `when` expression in Kotlin.
val x = 2
val result = when(x) {
1 -> "One"
2 -> "Two"
else -> "Other"
}
Why: `when` provides a cleaner, more versatile alternative to `switch`.
How: `when` matches a value against multiple conditions and returns a result or executes code blocks.
9. How do you create a singleton in Kotlin?
object Singleton {
val id = 42
fun show() = println("Singleton id: $id")
}
Singleton.show()
Why: Singletons ensure a single instance of a class is used throughout the app.
How: Use `object` declaration to automatically create a thread-safe singleton.
10. What are companion objects?
class MyClass {
companion object {
fun create(): MyClass = MyClass()
}
}
val instance = MyClass.create()
Why: Companion objects hold static-like members for a class.
How: Declare `companion object` inside a class to access methods and properties without an instance.
11. How do ranges work in Kotlin?
for (i in 1..5) {
println(i)
}
Why: Ranges provide a simple syntax for loops and range checks.
How: `1..5` creates a range from 1 to 5. Use `step`, `downTo`, or `until` for variations.
12. Explain destructuring declarations.
data class Point(val x: Int, val y: Int)
val (xCoord, yCoord) = Point(10, 20)
println("$xCoord, $yCoord") // 10, 20
Why: Destructuring improves code readability by extracting values from objects easily.
How: Data classes automatically provide component functions (`component1()`, `component2()`) used in destructuring.
13. What are sealed classes?
sealed class Result
data class Success(val data: String): Result()
object Failure: Result()
val res: Result = Success("Hello")
Why: Sealed classes restrict class hierarchies, ensuring exhaustive `when` checks.
How: Mark a class as `sealed` to limit subclassing to the same file, making pattern matching safer.
14. Explain inline functions.
inline fun measure(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
println("Time: ${System.currentTimeMillis() - start}ms")
}
Why: Inline functions reduce overhead by inlining function calls, beneficial for higher-order functions.
How: Use the `inline` keyword before the function. Useful when passing lambdas frequently.
15. What are reified type parameters?
inline fun printType() {
println(T::class)
}
printType() // prints "class kotlin.String"
Why: Normally, generic type information is erased at runtime, reified types preserve it.
How: Use `inline` with `reified` to access the type inside the function body.
16. How do coroutines differ from threads?
// Example: launching a coroutine
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000)
println("Coroutine done")
}
println("Main done")
}
Why: Coroutines are lightweight and more efficient than threads for concurrency.
How: Use `launch`, `async`, and `suspend` functions to manage asynchronous tasks without blocking threads.
17. Explain the `suspend` keyword.
suspend fun fetchData(): String {
delay(1000)
return "Data"
}
Why: `suspend` functions can pause and resume without blocking threads.
How: Mark functions as `suspend` to call other suspending functions like `delay` inside them.
18. How do you handle coroutine cancellations?
val job = launch {
repeat(1000) { i ->
println("Working $i")
delay(50)
}
}
delay(200)
job.cancel()
Why: Graceful cancellation saves resources and stops unnecessary work.
How: Use `job.cancel()` or cancellation checks (e.g., `ensureActive()`) within loops.
19. Explain the difference between `launch` and `async`.
val deferred = async {
delay(1000)
"Result"
}
println(deferred.await())
Why: `launch` is for fire-and-forget coroutines, `async` returns a `Deferred` result.
How: Use `async` when you need a result. `await()` it later to get the computed value.
20. What is `runBlocking`?
fun main() = runBlocking {
println("Start")
delay(1000)
println("End")
}
Why: `runBlocking` bridges coroutine and regular code by blocking the current thread until completion.
How: Use `runBlocking` in main or tests to run suspend functions without a coroutine environment.
21. Explain channels in coroutines.
val channel = Channel()
launch {
for (i in 1..5) channel.send(i)
channel.close()
}
launch {
for (value in channel) println(value)
}
Why: Channels allow safe communication between coroutines.
How: `send` and `receive` elements, closing channels once done.
22. What are Flow and its benefits?
fun simpleFlow(): Flow = flow {
for (i in 1..3) {
emit(i)
delay(100)
}
}
Why: Flows represent asynchronous streams of values, handling backpressure and suspensions gracefully.
How: Use `flow { emit(...) }` and collect with `collect()` in a suspend context.
23. How to handle exceptions in coroutines?
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
val job = GlobalScope.launch(handler) {
throw Exception("Error")
}
runBlocking { job.join() }
Why: Proper error handling ensures stable and predictable asynchronous code.
How: Use `CoroutineExceptionHandler` or try-catch within coroutine scopes.
24. Explain `withContext` function.
suspend fun readData() = withContext(Dispatchers.IO) {
// Perform IO operation
"Data"
}
Why: `withContext` switches the coroutine context, changing the thread pool for a specific block.
How: Provide a `Dispatcher` to run CPU or IO tasks off the main thread.
25. How do you make collections safer with Kotlin?
val list = listOf("A", "B", "C")
list.getOrNull(5) // returns null if index out of bounds
Why: Safer collection handling avoids runtime exceptions.
How: Use built-in safe access methods like `getOrNull` or `getOrElse`.
26. What is the difference between `List` and `MutableList`?
val immutableList: List = listOf(1,2,3)
val mutableList: MutableList = mutableListOf(1,2,3)
mutableList.add(4)
Why: Immutability improves code safety.
How: `List` is read-only. `MutableList` supports add, remove, etc.
27. How to filter collections?
val numbers = listOf(1,2,3,4,5)
val evens = numbers.filter { it % 2 == 0 } // [2,4]
Why: Functional operations on collections create clean, concise data transformations.
How: Use `filter`, `map`, `reduce` and other standard library functions.
28. Explain `map` function on collections.
val numbers = listOf(1,2,3)
val squared = numbers.map { it * it } // [1,4,9]
Why: `map` transforms each element according to a given function.
How: Pass a lambda to `map` to produce a new collection of transformed values.
29. How to deal with nullable collections?
val list: List = listOf("A", null, "C")
val nonNullList = list.filterNotNull() // ["A", "C"]
Why: Filtering nulls prevents null-related errors when processing collections.
How: Use `filterNotNull()` to remove all null entries.
30. How do you sort collections?
val nums = listOf(3,1,4,2)
val sortedNums = nums.sorted() // [1,2,3,4]
Why: Sorting data is a common requirement.
How: Use `sorted()`, `sortedBy()`, or `sortedWith()` with a custom comparator.
31. Explain `take` and `drop` functions.
val items = listOf("a","b","c","d","e")
val firstTwo = items.take(2) // ["a","b"]
val skipTwo = items.drop(2) // ["c","d","e"]
Why: `take` and `drop` help slice collections easily.
How: `take(n)` returns first n elements, `drop(n)` skips first n elements.
32. What is a lambda receiver?
fun buildString(block: StringBuilder.() -> Unit): String {
val sb = StringBuilder()
sb.block()
return sb.toString()
}
val result = buildString {
append("Hello")
append(" World")
}
Why: Lambda receivers let you call methods on an implied `this` object for DSL-like syntax.
How: Define the lambda parameter as `ClassName.() -> Unit`.
33. How do you handle exceptions in Kotlin?
try {
val result = riskyOperation()
} catch(e: Exception) {
println("Error: ${e.message}")
} finally {
println("Cleanup")
}
Why: Exception handling ensures robust error recovery.
How: Use `try-catch-finally` blocks. Kotlin doesn't force checked exceptions.
34. What are inline classes?
@JvmInline
value class Email(val value: String)
fun sendEmail(email: Email) { /* ... */ }
Why: Inline classes wrap values without runtime overhead, improving type safety.
How: Mark with `@JvmInline` and `value` keyword. At runtime, they behave like the underlying type.
35. Explain tail recursion.
tailrec fun factorial(n: Int, acc: Long = 1L): Long {
return if (n <= 1) acc else factorial(n-1, acc*n)
}
Why: Tail recursion prevents stack overflow by optimizing recursive calls.
How: Mark the function with `tailrec`, ensure the recursive call is the last operation.
36. How to create a type alias?
typealias Name = String
fun printName(name: Name) {
println(name)
}
Why: Type aliases give descriptive names to existing types for readability.
How: Use `typealias` keyword and treat it like a normal type afterwards.
37. What is the purpose of `lateinit`?
lateinit var user: User
fun initUser() {
user = User("Bob")
}
Why: `lateinit` allows for non-null var properties that are initialized later.
How: Use `lateinit` on var properties of non-primitive types and ensure initialization before use.
38. How do you convert a Kotlin class to a JSON string?
// Using kotlinx.serialization:
@Serializable
data class Person(val name: String, val age: Int)
val json = Json.encodeToString(Person("John", 30))
Why: Serialization is common in data exchange.
How: Use `kotlinx.serialization` library with `@Serializable` annotation and `Json` functions.
39. Explain generics in Kotlin.
class Box(val item: T)
val intBox = Box(42)
val strBox = Box("Hello")
Why: Generics provide type safety and reusability.
How: Use `
40. How do you use `is` operator for type checks?
val obj: Any = "Hello"
if (obj is String) {
println(obj.length) // Smart cast to String
}
Why: Type checks ensure safe casts.
How: `is` checks the type at runtime, and if true, the compiler smart casts the variable.
41. What are inline properties?
val currentTime: Long inline get() = System.currentTimeMillis()
Why: Inline properties avoid function call overhead for simple getters.
How: Use `inline` on property getters to inline code at call sites.
42. How to implement operator overloading?
data class Vector(val x: Int, val y: Int) {
operator fun plus(other: Vector) = Vector(x+other.x, y+other.y)
}
Why: Operator overloading makes code more natural for certain operations.
How: Implement functions with the `operator` keyword (e.g. `operator fun plus`).
43. How do you create a DSL in Kotlin?
fun html(block: HTML.() -> Unit): HTML {
val html = HTML()
html.block()
return html
}
html {
head {
title { "Page Title" }
}
body {
h1 { "Hello" }
}
}
Why: DSLs make domain-specific code more readable and expressive.
How: Use extension functions, lambda receivers, and builders to create DSL-like constructs.
44. Explain difference between `==` and `===`.
val a = "Hello"
val b = "Hello"
println(a == b) // true, structural equality
println(a === b) // might be true or false, reference equality
Why: Understanding equality prevents logical errors.
How: `==` checks structural equality, `===` checks referential equality.
45. How to create an enum class?
enum class Direction {
NORTH, SOUTH, EAST, WEST
}
val dir = Direction.NORTH
Why: Enums represent a fixed set of constants.
How: Use `enum class` and list constants. Enums can have properties and methods too.
46. How to provide a custom getter/setter?
var name: String = "Bob"
get() = field.toUpperCase()
set(value) {
field = value.trim()
}
Why: Custom getters/setters add validation or formatting.
How: Define `get()` and `set(value)` blocks for properties.
47. Explain lazy initialization.
val config by lazy {
loadConfig()
}
Why: Lazy loading defers expensive computations until needed.
How: Use `by lazy` to initialize a property upon first access, caching the result.
48. How to create a generic extension function?
fun List.penultimate(): T? =
if (size < 2) null else this[size - 2]
println(listOf(1,2,3).penultimate()) // 2
Why: Generic extensions add flexibility to data structures.
How: Add `
49. Explain `Nothing` type.
fun fail(): Nothing {
throw Exception("Error")
}
Why: `Nothing` indicates no value is returned, often for exceptions or infinite loops.
How: Return `Nothing` for functions that never return normally.
50. How to use `apply` scope function?
val person = Person().apply {
name = "Alice"
age = 25
}
Why: `apply` configures an object and returns it, making initialization concise.
How: `this` refers to the object inside `apply`. Useful for object creation and configuration.
51. Explain the `also` scope function.
val list = mutableListOf("a","b").also {
it.add("c")
}
Why: `also` lets you perform additional actions without changing the original receiver.
How: `also` passes the object as `it` and returns the object itself.
52. Difference between `let` and `run`?
val text: String? = "Hello"
text?.let { println(it) } // `it` is the receiver
text?.run { println(this) } // `this` is the receiver
Why: Both help with nullable handling and code blocks.
How: `let` uses `it`, `run` uses `this`. `run` is often used with non-null receivers directly.
53. Explain the `with` function.
val sb = StringBuilder()
with(sb) {
append("Hello")
append(" World")
}
println(sb.toString())
Why: `with` provides a clean syntax for operations on the same object.
How: `with` takes the object as a parameter and `this` is its receiver inside the block.
54. How to handle immutability in Kotlin?
val immutableList = listOf("a","b","c")
// immutableList.add("d") // compile error
Why: Immutability leads to safer, predictable code.
How: Use `val` and immutable collections (e.g. `listOf`) to prevent modifications.
55. Difference between `String` and `StringBuilder`?
var s = "Hello"
s += " World" // Creates new String
val sb = StringBuilder("Hello")
sb.append(" World") // Modifies in place
Why: `String` is immutable, `StringBuilder` is mutable and better for repeated modifications.
How: Use `StringBuilder` when building strings in loops to improve performance.
56. Explain `takeIf` and `takeUnless`.
val number = 10
val evenOrNull = number.takeIf { it % 2 == 0 }
val oddOrNull = number.takeUnless { it % 2 == 0 }
Why: They return the object if a condition is met, or null otherwise.
How: `takeIf(condition)` returns the object if condition is true, else null. `takeUnless` does the opposite.
57. How to create a custom annotation?
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotation(val info: String)
@MyAnnotation("Test")
class MyClass
Why: Annotations add metadata to code for frameworks or processing tools.
How: Use `annotation class` and specify targets and retention policies.
58. What are companion object extensions?
class MyClass {
companion object {}
}
fun MyClass.Companion.extensionFunc() = println("Extended companion")
MyClass.extensionFunc()
Why: Extending companion objects adds functionalities like static methods externally.
How: Use `MyClass.Companion` as the receiver type in the extension function.
59. How to ensure code runs only once using `lazy`?
val config by lazy {
println("Initializing...")
"Config Loaded"
}
println(config) // prints "Initializing..." then "Config Loaded"
println(config) // prints only "Config Loaded"
Why: `lazy` initialization ensures expensive computations run once, cached for subsequent calls.
How: Use `by lazy` delegate, which executes the block once and stores the result.
60. What is the purpose of `operator fun invoke()`?
class Invokable {
operator fun invoke() = println("Invoked!")
}
val inv = Invokable()
inv() // Calls operator invoke()
Why: `invoke` makes instances callable like functions, enabling custom call syntax.
How: Implement `operator fun invoke()` in a class to call instances directly.
61. How to decompile Kotlin to Java?
You can use the Kotlin plugin in IntelliJ or run `kotlinc -classpath . MyFile.kt -d MyFile.jar` and then use `javap` on the resulting classes.
Why: Understanding generated bytecode or Java code aids in debugging and optimization.
How: Tools like IntelliJ's "Show Kotlin Bytecode" help view the equivalent Java code.
62. Explain covariance and contravariance in generics.
interface Producer {
fun produce(): T
}
interface Consumer {
fun consume(item: T)
}
Why: Variance controls subtype relationships for generic types, ensuring type safety.
How: `out` indicates covariance, `in` indicates contravariance.
63. How to create a singleton with lazy initialization?
object MySingleton {
init {
println("Initialized")
}
}
Why: Singletons guarantee a single instance. Kotlin `object` are initialized lazily on first use.
How: Just refer to `MySingleton`; initialization occurs at that point.
64. Explain the `check` and `require` functions.
val x = 5
require(x > 0) { "x must be positive" }
check(x < 10) { "x must be less than 10" }
Why: They enforce conditions at runtime with clear, readable checks.
How: `require` is for validating arguments, `check` for internal invariants.
65. How to create a function reference?
fun greet(name: String) = "Hello $name"
val ref = ::greet
println(ref("Alice")) // "Hello Alice"
Why: Function references allow passing functions as values without lambdas.
How: Use `::functionName` to get a reference to a named function.
66. Explain `lateinit` properties testing.
class MyClass {
lateinit var name: String
}
val obj = MyClass()
// obj.name // Exception if accessed before initialization
Why: `lateinit` variables must be initialized before use.
How: Use `::name.isInitialized` to check if `lateinit` is initialized.
67. How to work with `requireNotNull`?
val maybeName: String? = "John"
val name = requireNotNull(maybeName) { "Name required" }
Why: `requireNotNull` asserts a variable is not null, otherwise throws an exception.
How: Provide a message lambda for better error messages if null.
68. What's the difference between `fold` and `reduce`?
val nums = listOf(1,2,3)
val sum = nums.reduce { acc, n -> acc + n } // 6
val sumFold = nums.fold(10) { acc, n -> acc + n } // 16 (initial=10)
Why: They accumulate collections into a single result.
How: `reduce` uses the first element as initial, `fold` takes an explicit initial value.
69. Explain `partition` on collections.
val (even, odd) = listOf(1,2,3,4,5).partition { it % 2 == 0 }
Why: Splits a collection into two lists based on a predicate.
How: `partition` returns a Pair of lists: first where predicate is true, second false.
70. How to handle parallel collections?
Kotlin doesn't have built-in parallel collection operations like Java Streams, but you can use `asSequence()` and external libraries or coroutines for concurrency.
Why: Parallelism improves performance on large data sets.
How: Convert to `Sequence`, or use coroutines to distribute work across multiple threads.
71. How to use `sealed interfaces`?
sealed interface Event
object Start : Event
object Stop : Event
Why: Sealed interfaces ensure a fixed set of implementations, allowing exhaustive checks.
How: Mark the interface `sealed`, define implementations in the same file.
72. How to enforce immutability with `val`?
val immutable = "constant value"
// immutable = "new value" // error
Why: Immutability enhances code safety by preventing unintended changes.
How: Use `val` for references that never reassign, ensuring stability.
73. Explain reflection usage in Kotlin.
import kotlin.reflect.full.memberProperties
data class User(val name:String, val age:Int)
val props = User::class.memberProperties
props.forEach { println(it.name) }
Why: Reflection dynamically inspects classes, methods, and properties at runtime.
How: Use `::class`, `memberProperties`, `functions` and `call` methods from `kotlin.reflect` library.
74. How to use `invoke` on function references?
fun hello() = "Hi"
val ref = ::hello
println(ref.invoke()) // "Hi"
Why: `invoke()` calls the referenced function.
How: Either `ref()` or `ref.invoke()` executes the referenced function.
75. Explain the `operator fun get()` for indexing.
class MyList(val items: List) {
operator fun get(index: Int) = items[index]
}
val ml = MyList(listOf("a","b"))
println(ml[1]) // "b"
Why: Operator overloading `get` allows `object[index]` syntax.
How: Define `operator fun get(index:Int)` and return the item.
76. What is `typeOf()`?
typeOf
is a function that retrieves a reified type at runtime, introduced in Kotlin 1.4.
inline fun printTypeOf() {
println(typeOf())
}
Why: Enables runtime type introspection without reflection overhead.
How: Use `reified` and `typeOf
77. How to compare `String` ignoring case?
val equal = "Hello".equals("hello", ignoreCase = true) // true
Why: Case-insensitive comparisons are common.
How: Use `equals` with `ignoreCase=true` or `compareTo(..., ignoreCase=true)`.
78. How to trim strings?
val trimmed = " Hello ".trim() // "Hello"
Why: Trimming removes unwanted whitespace.
How: `trim()`, `trimStart()`, `trimEnd()` remove whitespace from ends.
79. Explain `Regex` usage in Kotlin.
val regex = Regex("\\d+")
val match = regex.find("abc123xyz")?.value // "123"
Why: Regex handles complex string patterns matching and extraction.
How: Use `Regex(pattern)` and `find`, `matchEntire`, `replace` methods.
80. How to handle resource management?
File("test.txt").useLines { lines ->
lines.forEach { println(it) }
}
Why: Using resources safely avoids leaks.
How: `use` extension function closes resources automatically after use.
81. How to make collection operations lazy?
val seq = listOf(1,2,3).asSequence()
seq.map { it*2 }.filter { it>2 }.forEach { println(it) }
Why: Lazy evaluation improves performance on large data sets.
How: Convert to `Sequence` and use sequence operations which are lazy.
82. Explain `by` keyword in delegation.
interface Base {
fun printMessage()
}
class BaseImpl : Base {
override fun printMessage() = println("Base")
}
class Derived(b: Base): Base by b
Derived(BaseImpl()).printMessage() // "Base"
Why: Delegation composes behavior without inheritance complexity.
How: `class Derived(b:Base): Base by b` delegates Base methods to `b`.
83. How to create an array?
val arr = arrayOf(1,2,3)
val arr2 = IntArray(3) { it*2 } // [0,2,4]
Why: Arrays are fixed-size collections of elements.
How: Use `arrayOf()` or specialized arrays like `IntArray`, `Array
84. Explain the `let` function for nullables.
val name: String? = "Bob"
name?.let { println("Name length: ${it.length}") }
Why: `let` safely executes code if the object is non-null, avoiding null checks.
How: `it` is the non-null object inside `let` block.
85. What is `apply` vs `also` difference?
`apply` uses `this` as the context and returns the receiver. `also` uses `it` and returns the receiver. `apply` is for configuration, `also` for side-effects.
86. How to create a custom iterator?
class MyRange(val start:Int, val end:Int): Iterable {
override fun iterator() = object : Iterator {
var current = start
override fun hasNext() = current <= end
override fun next() = current++
}
}
for (i in MyRange(1,3)) println(i)
Why: Custom iterators integrate custom data structures into `for` loops.
How: Implement `Iterable
87. How to call Java code from Kotlin?
Kotlin is fully interoperable with Java. Just call Java classes and methods as if they were Kotlin. You may need nullability annotations for safety.
Why: Interoperability allows gradual migration and reuse of existing code.
How: Put both Kotlin and Java in the same module, use Kotlin code to call Java classes directly.
88. Explain `@JvmName` annotation.
@file:JvmName("MyUtils")
fun greet() = "Hello"
Why: `@JvmName` changes the generated class/method names for Java interop.
How: Place it at file-level or function-level to customize Java-facing names.
89. How to inline lambdas with `noinline` and `crossinline`?
inline fun foo(block: ()->Unit, noinline another: ()->Unit) {
block()
another()
}
Why: `noinline` prevents a lambda from being inlined. `crossinline` prevents non-local returns.
How: Use `noinline` on lambdas you do not want to inline. Use `crossinline` when lambda cannot return from the outer function.
90. Explain the difference between `Array` and `List` in Kotlin.
`Array` is a fixed-size, mutable structure. `List` is an immutable collection interface, `MutableList` is a mutable version. Lists are more commonly used for idiomatic Kotlin code.
Why: Each suits different needs. `Array` is more low-level, `List` is higher-level and safer.
How: Use `arrayOf()` for arrays, `listOf()` or `mutableListOf()` for lists.
91. How to read lines from a file in Kotlin?
File("data.txt").forEachLine {
println(it)
}
Why: Built-in IO operations simplify file handling.
How: Use `forEachLine()`, `readLines()`, or `useLines()` for file reading.
92. What is the use of `@JvmField`?
class MyClass {
@JvmField val id = 42
}
Why: `@JvmField` exposes a Kotlin property as a Java field without getters/setters.
How: Annotate the property with `@JvmField` for direct field access from Java.
93. Explain `@JvmOverloads` annotation.
class MyClass {
@JvmOverloads
fun greet(name:String = "Guest") {
println("Hello $name")
}
}
Why: `@JvmOverloads` generates multiple Java overloaded methods for default parameters.
How: Apply on a function with default parameters to call it easily from Java.
94. How to convert Kotlin objects to JSON and back?
@Serializable
data class Person(val name: String)
val jsonStr = Json.encodeToString(Person("Bob"))
val obj = Json.decodeFromString(jsonStr)
Why: Converting to/from JSON is crucial for data exchange.
How: Use `kotlinx.serialization` or other libraries like Gson or Moshi.
95. Explain multi-platform projects.
Kotlin Multiplatform allows sharing code across JVM, JS, and Native targets. Write common code once, and implement platform-specific parts separately.
Why: Increases code reuse and reduces development effort.
How: Set up Kotlin Multiplatform modules and specify targets and source sets.
96. How to measure execution time?
val start = System.currentTimeMillis()
// code
val end = System.currentTimeMillis()
println("Time: ${end - start} ms")
Why: Performance measurement helps optimize code.
How: Capture time before and after execution to calculate duration.
97. How to mock Kotlin classes in tests?
Use testing frameworks like Mockito or MockK. MockK supports Kotlin features better.
// Example with MockK
val mock = mockk()
every { mock.greet("Bob") } returns "Hi Bob"
Why: Mocking isolates code under test.
How: Use MockK or Mockito with Kotlin support.
98. Explain `synchronized` function in Kotlin.
val lock = Any()
synchronized(lock) {
// critical section
}
Why: Synchronization ensures thread safety in concurrent code.
How: `synchronized(lock)` creates a synchronized block guarded by `lock` object.
99. How to create a custom annotation processor?
Use Kotlin Symbol Processing (KSP) or KAPT. Implement `SymbolProcessor` or an annotation processor that reads annotations and generates code.
Why: Annotation processors automate repetitive code generation.
How: Implement processor interfaces, configure Gradle, run at compile time.
100. How to run Kotlin scripts?
kotlinc -script myScript.kts
Why: Kotlin scripts allow quick scripting without full project setup.
How: Use `.kts` file extension and run with `kotlinc -script` or use `kotlin` command.
Conclusion
These 100 Kotlin interview questions and code examples cover a wide range of topics, from basic syntax and null safety to advanced features like coroutines, DSLs, and annotation processing. Understanding the "why" and "how" behind these concepts ensures you can confidently tackle Kotlin challenges and write clean, efficient, and robust Kotlin code.