Title: Kotlin Programming — LearnCoachAssist

Q: What is null safety in Kotlin?
A: Null safety is a Kotlin feature that eliminates NullPointerException at compile time.
By default, variables cannot hold null. You must explicitly declare a type as nullable using ?, e.g. var name: String? = null.

Q: How do you declare a nullable type in Kotlin?
A: Append a ? to the type declaration:
var name: String? = null
Without the ?, the compiler will reject any attempt to assign null.

Q: What is the safe call operator in Kotlin?
A: The safe call operator ?. allows you to access a property or call a method on a nullable object without risking a NullPointerException:
val length = name?.length
If name is null, the expression returns null instead of throwing.

Q: What is the Elvis operator in Kotlin?
A: The Elvis operator ?: provides a default value when an expression is null:
val len = name?.length ?: 0
If the left side is null, the right side is used.

Q: What does the non-null assertion operator (!!) do?
A: The !! operator converts a nullable type to a non-null type and throws a KotlinNullPointerException if the value is null:
val len = name!!.length
Use it only when you are absolutely certain the value is not null.

Q: What is a data class in Kotlin?
A: A data class automatically generates equals(), hashCode(), toString(), copy(), and componentN() functions:
data class User(val name: String, val age: Int)
It is designed to hold immutable data.

Q: What methods does a Kotlin data class auto-generate?
A: A data class auto-generates:
- equals() and hashCode()
- toString() in the form User(name=John, age=30)
- copy() for creating modified copies
- componentN() functions for destructuring

Q: How does the copy() function work on a data class?
A: copy() creates a new instance with the same values, allowing you to override specific properties:
val user2 = user1.copy(age = 25)
This is useful for working with immutable data objects.

Q: What are extension functions in Kotlin?
A: Extension functions let you add new functions to existing classes without modifying their source code:
fun String.addExclamation(): String = this + "!"
Called as: "Hello".addExclamation() returns "Hello!".

Q: Can extension functions access private members of a class?
A: No. Extension functions are resolved statically and do not actually modify the class. They cannot access private or protected members of the receiver class.

Q: What is a sealed class in Kotlin?
A: A sealed class restricts which classes can inherit from it. All subclasses must be defined in the same file (or same package in Kotlin 1.5+):
sealed class Result
data class Success(val data: String) : Result()
data class Error(val msg: String) : Result()

Q: Why are sealed classes useful with when expressions?
A: Sealed classes allow the compiler to verify that a when expression covers all possible cases, eliminating the need for an else branch:
when (result) {
  is Success -> ...
  is Error -> ...
}

Q: How do sealed classes differ from enum classes?
A: - Enum classes: each value is a single instance with no varying state
- Sealed classes: each subclass can have multiple instances with different data and its own properties
Sealed classes are more flexible for modeling complex hierarchies.

Q: What is a companion object in Kotlin?
A: A companion object is declared inside a class using companion object { }. Its members can be accessed using the class name directly, similar to Java static members:
class MyClass { companion object { val TAG = "MyClass" } }
Access: MyClass.TAG.

Q: Can a companion object implement an interface?
A: Yes. A companion object can implement interfaces:
class MyClass {
  companion object : Factory<MyClass> {
    override fun create(): MyClass = MyClass()
  }
}
This is a powerful pattern for factory methods.

Q: What are higher-order functions in Kotlin?
A: A higher-order function takes a function as a parameter or returns a function:
fun operate(x: Int, op: (Int) -> Int): Int = op(x)
Called as: operate(5) { it * 2 } returns 10.

Q: What is a lambda expression in Kotlin?
A: A lambda is an anonymous function written as a literal:
val square: (Int) -> Int = { x -> x * x }
If there is only one parameter, you can use it:
val square: (Int) -> Int = { it * it }

Q: What does the inline keyword do for higher-order functions?
A: The inline keyword tells the compiler to copy the function body and the lambda body at the call site, avoiding the overhead of creating a function object:
inline fun execute(action: () -> Unit) { action() }
This improves performance for small lambdas.

Q: What is destructuring in Kotlin?
A: Destructuring lets you unpack an object into multiple variables in a single declaration:
val (name, age) = User("Alice", 30)
It relies on componentN() operator functions. Data classes provide these automatically.

Q: How does destructuring work in a for loop?
A: You can destructure entries in a loop:
for ((key, value) in map) {
  println("$key -> $value")
}
This works with any object that provides component1() and component2().

Q: What is the let scope function in Kotlin?
A: let executes a block on a non-null object and returns the lambda result. The object is referenced as it:
name?.let { println(it) }
Commonly used for null checks and transformations.

Q: What is the run scope function in Kotlin?
A: run executes a block where this refers to the object. It returns the lambda result:
val result = service.run {
  port = 8080
  connect()
}
Useful for object configuration and computing a result.

Q: What is the with scope function in Kotlin?
A: with is a non-extension function that takes an object and a lambda. Inside the block, this refers to the object:
with(builder) {
  setTitle("Hello")
  setMessage("World")
}
Returns the lambda result. Best for calling multiple methods on an object.

Q: What is the apply scope function in Kotlin?
A: apply executes a block on an object (referenced as this) and returns the object itself:
val user = User().apply {
  name = "Alice"
  age = 30
}
Ideal for object initialization.

Q: What is the also scope function in Kotlin?
A: also executes a block on an object (referenced as it) and returns the object itself:
val list = mutableListOf(1, 2).also {
  println("Initial list: $it")
}
Best for side effects like logging or validation.

Q: How do let, run, apply, also, and with differ?
A: - let: ref = it, returns lambda result
- run: ref = this, returns lambda result
- with: ref = this, returns lambda result (non-extension)
- apply: ref = this, returns the object
- also: ref = it, returns the object

Q: What are coroutines in Kotlin?
A: Coroutines are a lightweight concurrency framework. They allow writing asynchronous code sequentially without blocking threads:
launch { delay(1000); println("World") }
They are managed by a coroutine dispatcher and are much cheaper than threads.

Q: What is a suspend function in Kotlin?
A: A suspend function can be paused and resumed without blocking a thread:
suspend fun fetchData(): String { ... }
Suspend functions can only be called from another suspend function or a coroutine builder like launch or async.

Q: What is the difference between launch and async in coroutines?
A: - launch: starts a coroutine that returns a Job (no result value). Fire-and-forget.
- async: starts a coroutine that returns a Deferred<T>. Call .await() to get the result.
Use async when you need a return value.

Q: What is a CoroutineScope in Kotlin?
A: A CoroutineScope defines the lifecycle of coroutines. When the scope is cancelled, all its coroutines are cancelled:
val scope = CoroutineScope(Dispatchers.IO)
Common scopes: GlobalScope, viewModelScope, lifecycleScope.

Q: What are coroutine dispatchers in Kotlin?
A: Dispatchers determine which thread a coroutine runs on:
- Dispatchers.Main — UI thread
- Dispatchers.IO — optimized for I/O operations
- Dispatchers.Default — CPU-intensive work
- Dispatchers.Unconfined — not confined to any thread

Q: What is structured concurrency in Kotlin coroutines?
A: Structured concurrency ensures that coroutines are scoped to a parent. If the parent is cancelled or fails, all child coroutines are automatically cancelled. This prevents coroutine leaks and simplifies error handling.

Q: What is delegation in Kotlin using the by keyword?
A: The by keyword implements the delegation pattern. A class can delegate interface implementation to another object:
class Derived(b: Base) : Base by b
All interface methods are forwarded to b automatically.

Q: What are delegated properties in Kotlin?
A: Delegated properties use by to delegate get/set logic:
val name: String by lazy { computeName() }
Built-in delegates include lazy, observable, and vetoable. You can create custom delegates by implementing getValue()/setValue().

Q: How does the lazy delegate work in Kotlin?
A: lazy initializes a property on first access and caches the result:
val heavy: String by lazy { expensiveComputation() }
By default it is thread-safe (LazyThreadSafetyMode.SYNCHRONIZED).

Q: How does the observable delegate work in Kotlin?
A: Delegates.observable triggers a callback whenever the property changes:
var name: String by Delegates.observable("initial") { prop, old, new ->
  println("$old -> $new")
}
Useful for reacting to state changes.

Q: What is the difference between val and var in Kotlin?
A: - val — declares a read-only (immutable reference) variable. Cannot be reassigned.
- var — declares a mutable variable. Can be reassigned.
Example:
val pi = 3.14
var count = 0; count++

Q: What are Kotlin's basic data types?
A: Kotlin's basic types:
- Numbers: Int, Long, Short, Byte, Float, Double
- Boolean: true / false
- Char: single character 'A'
- String: text "Hello"
All types are objects — no primitives at the language level.

Q: What is string interpolation in Kotlin?
A: String interpolation lets you embed expressions inside strings using $:
val name = "Alice"
println("Hello, $name!")
For complex expressions, use curly braces: "Length: ${name.length}"

Q: What is the when expression in Kotlin?
A: when replaces Java's switch and is more powerful:
when (x) {
  1 -> "one"
  2, 3 -> "two or three"
  in 4..10 -> "between 4 and 10"
  is String -> "it's a string"
  else -> "unknown"
}
It can be used as a statement or expression.

Q: What are Kotlin ranges and how are they used?
A: Ranges are created with .. or until:
for (i in 1..5) // 1,2,3,4,5
for (i in 1 until 5) // 1,2,3,4
for (i in 5 downTo 1 step 2) // 5,3,1
Ranges also support in checks: if (x in 1..10).

Q: What is smart casting in Kotlin?
A: After an is check, the compiler automatically casts the variable:
if (obj is String) {
  println(obj.length) // no explicit cast needed
}
This works with when, if, and && / || expressions.

Q: What is the difference between == and === in Kotlin?
A: - == checks structural equality (calls equals())
- === checks referential equality (same object in memory)
val a = "hi"; val b = "hi"
a == b is true; a === b may be true due to string interning.

Q: How do you define a singleton in Kotlin?
A: Use the object declaration:
object DatabaseManager {
  fun connect() { ... }
}
Called as: DatabaseManager.connect()
The instance is created lazily and is thread-safe.

Q: What are Kotlin collections and their mutable variants?
A: Kotlin distinguishes read-only and mutable collections:
- List / MutableList
- Set / MutableSet
- Map / MutableMap
Use listOf(), mutableListOf(), mapOf(), mutableMapOf() to create them.

Q: What are common collection operations in Kotlin?
A: Kotlin provides rich functional operations:
val nums = listOf(1, 2, 3, 4, 5)
- nums.filter { it > 2 } → [3, 4, 5]
- nums.map { it * 2 } → [2, 4, 6, 8, 10]
- nums.reduce { acc, i -> acc + i } → 15
- nums.groupBy { it % 2 }

Q: What is a Kotlin interface and how is it different from Java's?
A: Kotlin interfaces can contain abstract methods and default implementations as well as abstract properties:
interface Clickable {
  val label: String
  fun click()
  fun showOff() = println("Clickable!")
}
A class can implement multiple interfaces.

Q: What is the difference between open, abstract, and sealed classes?
A: - open: can be inherited; not abstract, can be instantiated
- abstract: cannot be instantiated; may have abstract and concrete members
- sealed: like abstract but restricts subclasses to the same file/package
By default, Kotlin classes are final.

Q: What is a type alias in Kotlin?
A: A type alias provides an alternative name for an existing type:
typealias UserMap = Map<String, User>
typealias OnClick = (View) -> Unit
They simplify complex generic types and function type signatures without creating new types.

Q: What are the takeIf and takeUnless functions in Kotlin?
A: takeIf returns the object if the predicate is true, otherwise null:
val even = number.takeIf { it % 2 == 0 }
takeUnless does the opposite — returns the object if the predicate is false:
val odd = number.takeUnless { it % 2 == 0 }

Q: What does the let scope function return when given a nullable receiver?
A: it returns the lambda result (the last expression) when the receiver is non-null; if the receiver is null, the whole let call returns null without executing the block.

Q: When does the right-hand side of the Elvis operator get evaluated?
A: Only when the left-hand side expression is null. If the left side is non-null, the right side is not evaluated, which makes ?: useful for default values and lazy fallbacks.

Q: What is the precedence of the Elvis operator (?:) in Kotlin?
A: ?: has lower precedence than most binary operators but is right-associative. A common idiom is a ?: b ?: c which evaluates to the first non-null value.

Q: How does the !! operator interact with generic types?
A: !! forces a non-null type that the smart cast system trusts throughout its scope. The thrown exception is KotlinNullPointerException (not NullPointerException), and the stack trace points to the !! site.

Q: What is the difference between let and also for null safety?
A: Both accept a nullable receiver via safe call, but let returns the lambda result while also returns the original object. Use also for side effects that keep the object flowing through a chain.

Q: How do you chain multiple safe calls in Kotlin?
A: Each ?. short-circuits independently:
val city = user?.address?.city
If any link is null, the whole expression evaluates to null. You can mix with Elvis: user?.address?.city ?: "Unknown".

Q: What is the purpose of the platform type in Kotlin/Java interop?
A: A platform type (e.g. String!) is generated when Kotlin calls Java code whose nullability annotations are missing or unclear. The compiler treats it as both nullable and non-nullable, leaving the choice to the caller — which can lead to NPEs at runtime.

Q: How do you mark a Java type as non-null when called from Kotlin?
A: Use JetBrains' @NotNull annotation (or JSR-305 @Nonnull / AndroidX @NonNull) on the Java declaration. Kotlin then sees the type as non-nullable and rejects null assignments at compile time.

Q: How do you mark a Java parameter as nullable from Kotlin?
A: Annotate the Java parameter with @Nullable (JetBrains / JSR-305 / AndroidX). Kotlin will then require safe-call or null checks when the value is used as a non-null type.

Q: What is the nothing type in Kotlin?
A: Nothing is a type with no instances. It is used for functions that never return — they always throw or terminate the process:
fun fail(msg: String): Nothing = throw IllegalStateException(msg)
Nothing is a subtype of every other type, so a Nothing expression can be used anywhere a return value is expected.

Q: What is the unit type in Kotlin?
A: Unit is Kotlin's equivalent of void. It is a real type with a single value (Unit itself) and can be used as a return type or generic parameter. If a function returns Unit, you may omit the type declaration.

Q: What is the any type in Kotlin?
A: Any is the root of the Kotlin type hierarchy, equivalent to Object in Java. Every non-nullable type is a subtype of Any. Any? is the supertype of all types, including nullable ones.

Q: How do you check if a variable is null in Kotlin?
A: Use the == or != operator with null:
if (name != null) { println(name.length) }
After this check, the compiler performs a smart cast so you can use name as a non-null type inside the block.

Q: What is a reified type parameter in Kotlin?
A: reified lets a generic type be accessed at runtime — normally erased by the JVM. The function must be inline:
inline fun <reified T> isType(value: Any) = value is T
Common uses: is T checks, T::class.java, and constructing instances.

Q: What is the noinline keyword used for?
A: noinline tells the compiler not to inline a specific lambda parameter of an inline function:
inline fun foo(action: () -> Unit, noinline logger: () -> Unit) { ... }
This is needed when the lambda is stored or passed elsewhere.

Q: What is the crossinline keyword used for?
A: crossinline prevents a lambda from doing non-local returns (i.e. return from the enclosing function). It is required when the lambda is invoked from a different context:
inline fun runBlock(crossinline block: () -> Unit) { object : Thread() { override fun run() = block() }.start() }

Q: What are infix functions in Kotlin?
A: Functions marked infix can be called using infix notation without a dot or parentheses:
infix fun Int.times(str: String) = str.repeat(this)
val s = 3 times "Hi " // "Hi Hi Hi "
Requirements: member or extension, single parameter, no default args, no varargs.

Q: What is the operator overloading syntax in Kotlin?
A: Mark a function with the operator keyword and a predefined name:
operator fun plus(other: Vec2) = Vec2(x + other.x, y + other.y)
val c = a + b translates to a.plus(b). Kotlin supports plus, minus, times, div, rem, compareTo, get/set, contains, invoke, and more.

Q: How does operator overloading for comparison work?
A: Override compareTo:
operator fun compareTo(other: Version) = this.major.compareTo(other.major)
Then <, <=, >, >=, and sort/sorted use it automatically.

Q: What is the invoke operator in Kotlin?
A: invoke lets an object be called like a function:
class Greeter { operator fun invoke(name: String) = "Hi $name" }
val g = Greeter(); println(g("Alice"))
Useful for DSL builders, callbacks, and function-like configuration objects.

Q: What are Kotlin property accessors and how are they customized?
A: Kotlin auto-generates get() and set() for properties. You can override them:
val name: String get() = "$first $last"
var password: String set(value) { if (value.length >= 8) field = value }
Inside a setter, the backing field is accessed via field.

Q: What is the backing field requirement for a Kotlin property?
A: A backing field is generated only if you use the default accessor or use field inside a custom accessor. A computed property with no field reference is just a function with property syntax.

Q: What are the lateinit and by lazy differences?
A: - lateinit var: mutable, must be set before use, throws UninitializedPropertyAccessException if accessed early. Useful for DI / Android views.
- lazy { }: read-only, thread-safe by default, initialized on first access. Use for expensive immutable values.

Q: How do you check if a lateinit property has been initialized?
A: Call ::property.isInitialized:
if (::adapter.isInitialized) { adapter.refresh() }
This works for top-level, class-level, and local lateinit properties.

Q: Can a primitive type be lateinit in Kotlin?
A: No. lateinit only works with non-null reference types — Int, Boolean, etc. cannot be lateinit. Use a nullable wrapper or a sentinel value instead.

Q: What is the purpose of the const keyword in Kotlin?
A: const val declares a compile-time constant:
const val MAX_USERS = 100
Requirements: top-level or in an object / companion object, value must be a primitive or String, and the initializer must be a literal. Useful for annotations and switch-like when cases.

Q: What is the difference between const val and val?
A: - const val: compile-time inlined into call sites, only primitives/Strings, only at top level or in objects.
- val: read-only reference, evaluated at runtime, may have any type or custom getter, can be inside any class.

Q: What is a primary constructor in Kotlin?
A: Declared in the class header:
class User(val name: String, val age: Int)
It can have default values, visibility modifiers, and @Annotations but cannot contain code — use an init block for logic.

Q: What is a secondary constructor in Kotlin?
A: Defined inside the class body with the constructor keyword:
class User { constructor(name: String) : this(name, 0) { ... } }
Each secondary constructor must delegate to the primary via this(...) or to another secondary constructor.

Q: What is an init block in Kotlin?
A: An init { } block runs as part of primary construction, in declaration order with property initializers. Multiple init blocks are allowed, and they share the primary constructor's parameter scope.

Q: What is the order of execution in a Kotlin class with init blocks?
A: 1) Property initializers and init blocks run in declaration order. 2) Secondary constructors run after delegation completes. Code in init always sees constructor parameters in scope.

Q: What is a nested class vs an inner class in Kotlin?
A: - Nested class (default): equivalent to Java static nested class. No reference to the outer instance.
- Inner class (inner class): holds an implicit reference to the outer instance, accessed via this@Outer.

Q: What does the override keyword do in Kotlin?
A: Marks a member as an override of a parent member. The compiler enforces that the parent member is open and the signatures match. override members are open by default; add final to close them again.

Q: How do you call a parent class implementation from an override?
A: Use the super keyword:
override fun click() { super.click(); extra() }
For interfaces, super<InterfaceName>.method() disambiguates when multiple parents provide the same method.

Q: What is the difference between object expression and object declaration?
A: - Object expression: an anonymous class created on the fly: val o = object : Foo() { override fun bar() = 1 }
- Object declaration: a named singleton: object Logger { fun log(m: String) = ... }
Object expressions are eager but local; declarations are lazy and global.

Q: What is a companion object's relationship to its class?
A: A companion object is initialized when the outer class is loaded, and its members can be accessed as if they were static. Each class can have at most one companion object.

Q: What is the @JvmStatic annotation used for?
A: @JvmStatic on a member of an object or companion object generates an actual Java static method/field, so Java callers can use MyClass.method() instead of MyClass.Companion.method().

Q: What is the @JvmField annotation used for?
A: @JvmField exposes a Kotlin property as a public Java field with no getter/setter. Useful for interop with libraries that expect public fields (e.g. some annotation processors, Lombok, or third-party JSON libraries).

Q: What is the @JvmOverloads annotation used for?
A: @JvmOverloads generates Java-style overloads for default parameters:
fun foo(a: Int, b: Int = 0, c: Int = 0) becomes three Java methods: foo(int), foo(int, int), foo(int, int, int).

Q: What is the @Throws annotation used for?
A: @Throws(IOException::class) on a Kotlin function tells the compiler to add a throws clause to the generated Java bytecode. Kotlin itself does not have checked exceptions, so Java callers see checked-exception declarations.

Q: What is the @JvmName annotation used for?
A: @JvmName("customName") changes the name of the generated JVM method, preventing signature clashes, helping Java interop, or shortening long names that exceed the JVM limit.

Q: What is the @SerialName annotation used for?
A: @SerialName("user_id") on a property or class overrides the name used by Kotlinx Serialization, decoupling Kotlin identifiers from the on-wire JSON / protobuf / CBOR field names.

Q: What is the purpose of the @Serializable annotation?
A: @Serializable (from kotlinx.serialization) marks a class as eligible for automatic encodeToString/decodeFromString operations. The compiler plugin generates the serializer at compile time.

Q: What is the difference between List and MutableList?
A: - List<T>: read-only interface — no add, remove, or clear methods.
- MutableList<T>: extends List with mutation methods.
At runtime, listOf(...) may return an ArrayList — but you cannot call mutating methods through the List reference.

Q: How do you create an empty list in Kotlin?
A: - emptyList<Int>() — a reusable read-only singleton (preferred for performance).
- listOf<Int>() — also empty but allocates a wrapper.
- mutableListOf<Int>() — empty and mutable.

Q: What is the difference between arrayOf and listOf?
A: - arrayOf(1, 2, 3) returns a Kotlin Array<Int> — boxed integers, fixed size, mutable contents.
- listOf(1, 2, 3) returns a List<Int> — backed by an ArrayList at runtime, immutable view.

Q: How do you create a primitive array in Kotlin?
A: Use type-specific factories: intArrayOf(1, 2, 3), longArrayOf(...), doubleArrayOf(...), charArrayOf(...), booleanArrayOf(...). These avoid boxing and are more efficient than Array<Int>.

Q: How do you create an Array with a size and initializer in Kotlin?
A: Use the Array(size) { index -> ... } constructor:
val squares = Array(5) { i -> (i * i).toString() }
The lambda receives the index and is called for every position at construction time.

Q: What does the sequence builder do in Kotlin?
A: sequenceOf(...) or sequence { yield(...); yieldAll(...) } produces a lazy iterable. Each element is computed on demand, allowing efficient chained operations on large or infinite streams.

Q: What is the difference between list and sequence in Kotlin?
A: - List: eager — every intermediate operation (map, filter) creates a new list.
- Sequence: lazy — operations are fused and elements are produced one at a time.
Use sequences for large collections or when many chained operations would otherwise allocate many intermediate lists.

Q: What is the associateWith function?
A: associateWith builds a Map from a list's elements to values produced by a transform:
listOf("a", "b").associateWith { it.length } → {"a"=1, "b"=1}
It is the inverse of associateBy.

Q: What is the chunked function on collections?
A: chunked(size) splits a collection into lists of the given size:
(1..7).toList().chunked(3) → [[1,2,3], [4,5,6], [7]]
Useful for batching network calls or paginating a UI.

Q: What does the windowed function do?
A: windowed(size, step, partialWindows) produces overlapping or stepped slices of a collection:
(1..5).toList().windowed(3) → [[1,2,3], [2,3,4], [3,4,5]]
Add step to skip elements, partialWindows = true to keep short tails.

Q: What is the zip function in Kotlin collections?
A: zip pairs elements from two collections of equal length into a List<Pair<A,B>>:
listOf("a","b").zip(listOf(1,2)) → [(a,1), (b,2)]
Stops at the shorter collection. zipWithNext pairs adjacent elements within a single collection.

Q: What does the flatMap function do?
A: flatMap maps each element to an Iterable and concatenates the results into a single flat list:
listOf("ab", "cd").flatMap { it.toList() } → [a, b, c, d]
Equivalent to map followed by flatten.

Q: What is the difference between fold and reduce?
A: - reduce: combines elements starting with the first one; throws if the collection is empty.
- fold: like reduce but takes an explicit initial accumulator; safe for empty collections.
listOf(1,2,3).reduce { a, b -> a + b } → 6
listOf<Int>().fold(0) { a, b -> a + b } → 0

Q: What is the difference between map and mapNotNull?
A: - map: applies the transform to every element and keeps all results.
- mapNotNull: applies the transform and discards any nulls from the result, returning a non-null list.
listOf("1", "x", "3").mapNotNull { it.toIntOrNull() } → [1, 3]

Q: How does the partition function work?
A: partition splits a collection into a Pair of two lists based on a predicate:
listOf(1,2,3,4).partition { it % 2 == 0 } → ([2, 4], [1, 3])
The first list contains matches, the second contains the rest.

Q: What does the groupBy function return?
A: A Map<K, List<T>> where K is the key produced by the selector:
listOf("apple", "ant", "banana").groupBy { it.first() } → {a=[apple, ant], b=[banana]}
Each key maps to all elements that share it.

Q: What is the firstOrNull vs first difference?
A: - first: returns the first element or throws NoSuchElementException.
- firstOrNull: returns the first element or null if the collection is empty (or no element matches the predicate).
Prefer firstOrNull when emptiness is possible.

Q: What is the singleOrNull function used for?
A: singleOrNull returns the only element of a collection, or null if there are zero or more than one. Useful for asserting uniqueness: users.singleOrNull { it.id == targetId }.

Q: What does the getOrElse function do?
A: getOrElse retrieves an element at an index (or by key for maps) or computes a fallback from a lambda:
map.getOrElse("missing") { defaultValue }
Preferable to ?: default when the default is expensive to compute.

Q: What is the difference between to and into for builders?
A: - apply / also return the receiver (use to build and pass on the object).
- let / run return the lambda result (use when you want to compute something new).
Choose based on what the chain needs to produce.

Q: What is the apply function ideal for?
A: Object initialization and configuration:
val dialog = AlertDialog.Builder(ctx).apply {
  setTitle("Hi")
  setMessage("World")
}.create()
Because it returns the receiver, it composes well in builder chains.

Q: What is a top-level function in Kotlin?
A: A function declared outside any class, package, or file scope. By default it is public and is compiled to a static method on a synthetic class named after the file (FileKt). Useful for utility functions and keeping the global namespace clean.

Q: What are top-level properties in Kotlin?
A: val or var declared at file level. They act like global constants or variables but can be private, internal, or const val. Accessed as MyFile.PROPERTY from Java.

Q: What is package-level visibility in Kotlin?
A: Visibility modifiers default to public; internal means visible within the same module. There is no package-private keyword like Java — to restrict to a package, declare the member internal or use a private file-level extension.

Q: How does the internal visibility modifier work?
A: internal members are visible anywhere in the same Gradle/IntelliJ module, but invisible from other modules. It is Kotlin's unit of encapsulation for libraries — a stronger default than public but less restrictive than protected.

Q: What is the protected visibility modifier?
A: protected members are visible in the declaring class and its subclasses only. Unlike Java, they are not visible to other code in the same package.

Q: How do you make a Kotlin class act as a builder?
A: Return this (or apply) from setter-like methods:
class Query { var limit = 10; fun limit(v: Int) = apply { limit = v } }
Callers write Query().limit(5).select("*").

Q: What is the difference between arrayListOf and mutableListOf?
A: Both return a mutable list, but:
- arrayListOf returns a Java ArrayList directly (java.util.ArrayList).
- mutableListOf returns the least specific mutable list type, allowing the implementation to be optimized (e.g. ArrayList, LinkedList on JVM).

Q: How do you convert a list to a set in Kotlin?
A: Call .toSet() or .toHashSet():
listOf(1, 2, 2, 3).toSet() → [1, 2, 3]
Use toMutableSet() if you need a mutable set. toHashSet() skips wrapper allocation by using the specific implementation.

Q: How do you create a map from two lists in Kotlin?
A: Use zip + toMap:
keys.zip(values).toMap()
If keys may collide, use .toMap(mutableMapOf()) and handle duplicates, or use associate with a transform.

Q: What is the associate function on collections?
A: associate returns a Map built by a transform that produces both key and value:
listOf(1,2,3).associate { it to it * 2 } → {1=2, 2=4, 3=6}
Last duplicate key wins.

Q: What does the distinct function return?
A: A list containing each element of the original only once, preserving first-occurrence order:
listOf(1, 2, 2, 3, 3, 3).distinct() → [1, 2, 3]
distinctBy { selector } deduplicates by the selected key.

Q: What is the difference between sorted and sortedDescending?
A: - sorted returns a list in ascending order (natural ordering, requires Comparable).
- sortedDescending returns the same elements in descending order.
Both allocate a new list; the original is unchanged.

Q: How do you sort by multiple criteria in Kotlin?
A: Chain sortedWith with compareBy + thenBy / thenDescending:
list.sortedWith(compareBy<User> { it.lastName }.thenBy { it.firstName })
This sorts first by last name, then by first name as a tiebreaker.

Q: What does the reversed function do?
A: reversed() returns a new list with elements in reverse order. Use asReversed() on a MutableList for a live view that reflects later mutations.

Q: What is the difference between forEach and forEachIndexed?
A: - forEach { item -> ... }: iterates values.
- forEachIndexed { i, item -> ... }: also exposes the 0-based index.
Prefer a normal for loop when you need break, continue, or return.

Q: How do you break out of a forEach lambda?
A: You cannot use break directly inside forEach. Options: use a normal for loop, use return@forEach to skip the current iteration, or wrap the work in run { ... } and use a labeled return@run.

Q: What is a labeled return in Kotlin?
A: A return with a label, used to exit a non-local scope:
listOf(1,2,3).forEach lit@{ if (it == 2) return@lit; println(it) }
By default, return@forEach uses the function name as an implicit label.

Q: How do you iterate over a map in Kotlin?
A: - for ((k, v) in map) { ... } — destructured entry iteration.
- map.forEach { (k, v) -> ... } — lambda form.
- Iterate keys: for (k in map.keys). Values: for (v in map.values).

Q: What is the getValue vs getOrDefault difference for maps?
A: - map[key] returns null when missing.
- map.getValue(key) throws NoSuchElementException when missing.
- map.getOrDefault(key, default) returns the supplied default.

Q: What is the getOrPut function on a mutable map?
A: getOrPut(key) { computeDefault() } returns the value for key, computing and inserting the default via the lambda if the key is missing:
counts.getOrPut(word) { 0 } → existing count, or 0 inserted.

Q: How do you remove a key from a map only if it matches a value?
A: Use the overload that takes both:
map.remove(key, value)
Returns true if the entry was removed. Useful for atomic compare-and-delete semantics.

Q: What is the result type of try-catch in Kotlin?
A: The try block is an expression. If all branches are present, the type is the common supertype of the try and catch result types:
val n = try { s.toInt() } catch (e: Exception) { -1 }

Q: How does Kotlin handle checked exceptions?
A: Kotlin has no checked exceptions. You can throw and catch any exception without declaring it. The compiler will not force callers of a function to catch or rethrow IOException, SQLException, etc.

Q: What is Nothing used for in a throw expression?
A: throw has return type Nothing. Because Nothing is a subtype of every other type, you can throw in an Elvis branch:
val name = input ?: throw IllegalArgumentException("name required")

Q: What is the result of a when expression with no matching branch and no else?
A: If when is used as an expression (its result is assigned), the compiler requires an else branch. Without it, the code does not compile. As a statement, unmatched values are simply ignored.

Q: What is the difference between for and while in Kotlin?
A: Both compile to similar bytecode, but for iterates over an iterator (range, array, collection, sequence) and is usually more idiomatic. while / do-while is for condition-driven loops with manual state.

Q: How do you iterate with a custom step in Kotlin?
A: Use step on a range:
for (i in 0..20 step 5) println(i) // 0, 5, 10, 15, 20
For a decreasing range, use downTo: for (i in 20 downTo 0 step 5).

Q: What is the difference between step and downTo?
A: - step n controls the increment of any range.
- downTo reverses the direction (descending range).
They can combine: for (i in 10 downTo 0 step 2) → 10, 8, 6, 4, 2, 0.

Q: What is a Char.isLetterOrDigit check used for?
A: Validating that a character is alphanumeric without manual ASCII math:
if (c.isLetterOrDigit()) append(c)
Other useful extensions: isDigit(), isLetter(), isUpperCase(), isWhitespace().

Q: How do you convert a String to Int safely in Kotlin?
A: Use toIntOrNull() which returns null on parse failure:
val n = "42".toIntOrNull() // 42
val n = "abc".toIntOrNull() // null
Contrast with toInt() which throws NumberFormatException.

Q: What does the joinToString function do?
A: Builds a single string from a collection with a separator, prefix, suffix, and optional transforms:
listOf(1,2,3).joinToString(prefix = "<", postfix = ">", separator = ", ") // "<1, 2, 3>"
Useful for logs, debug output, and quick formatting.

Q: What is the difference between substring and substringBefore/After?
A: - substring(startIndex, endIndex) takes raw indices and may throw.
- substringBefore(delim) / substringAfter(delim) return the text before/after the first occurrence of a delimiter (with optional missingDelimiterValue).
Delimiter-based methods are safer for parsing.

Q: What does the replace function on String support?
A: - Literal: "abc".replace("a", "x") → "xbc".
- Regex: "abc123".replace(Regex("\\d+"), "?") → "abc?".
- Range: "hello".replaceRange(0, 1, "H") → "Hello".

Q: What is the String.split function with a limit?
A: "a,b,c,d".split(",", limit = 2) → ["a", "b,c,d"]
The limit parameter caps the number of parts returned; remaining delimiters are kept in the last element. Without limit, the default is 0 (no cap).

Q: How do you build a String efficiently in Kotlin?
A: Use a StringBuilder or a buildString { append(...) } block:
val s = buildString { repeat(100) { append("x") } }
Avoid + concatenation in tight loops — each + allocates a new String.

Q: What is the difference between buildString and StringBuilder?
A: buildString { ... } is an inline function that creates a StringBuilder, runs the lambda with it as this, and returns toString(). StringBuilder is the underlying type — use it directly only when reusing instances for performance.

Q: What is a multi-line raw string in Kotlin?
A: Triple-quoted strings """ ... """ can span multiple lines and ignore escapes:
val s = """
  first
  second
"""
Call .trimIndent() to remove common leading whitespace, or .trimMargin("|") with a custom prefix.

Q: How do raw strings handle string interpolation?
A: Triple-quoted strings still support $variable and ${expr}:
val s = """Hello $name, you owe $$amount"""
To write a literal $ in a raw string, escape it: \$.

Q: What is the String?.toString() idiom?
A: someNullable?.toString() never throws — if the receiver is null, the whole expression is null. This is safer than someNullable.toString(), which would throw NPE.

Q: What is the difference between CharSequence and String?
A: String is a concrete, immutable sequence of Char. CharSequence is the interface implemented by String, StringBuilder, and StringBuffer. Library functions that only need to read chars should accept CharSequence for flexibility.

Q: What is a Regex in Kotlin and how is it used?
A: Construct with Regex("pattern") or "pattern".toRegex():
val r = Regex("""\d+""")
Use r.containsMatchIn(s), r.find(s), r.findAll(s), or r.replace(s, "x").

Q: What does the find function on a Regex return?
A: A MatchResult? — the first match or null:
val match = Regex("""(\w+)@(\w+)""").find(email)
Access groups via match.groupValues[1] or destructuring: val (user, domain) = match.destructured.

Q: What is the runCatching function in Kotlin?
A: runCatching { ... } wraps a block in a try/catch and returns a Result<T>:
runCatching { File("x").readText() }.getOrElse { "" }
You can chain .onSuccess { }, .onFailure { }, .getOrNull(), .getOrElse { }.

Q: What is the difference between runCatching and try-catch?
A: runCatching catches all Throwable (including Error), which is rarely what you want. Prefer try { } catch (e: IOException) { } for specific exception handling, and reserve runCatching for quick "may or may not work" chains.

Q: What does the getOrThrow function on Result do?
A: It returns the success value or rethrows the captured exception:
val data = runCatching { api.fetch() }.getOrThrow()
Useful when you want a Result-style chain to end in a thrown exception.

Q: What is the coroutine builder coroutineScope used for?
A: coroutineScope { ... } creates a child scope: all launched children complete (or are cancelled) before the block returns. Throws the first failure if any child fails. It is the structural way to wait for parallel work:
val (a, b) = coroutineScope { val x = async { fetchA() }; val y = async { fetchB() }; x.await() to y.await() }

Q: What is the difference between coroutineScope and supervisorScope?
A: - coroutineScope: child failure cancels siblings and propagates the exception.
- supervisorScope: child failure is isolated; siblings and the scope continue. The parent only fails if you await the failed child.

Q: What is the withContext function used for?
A: withContext(Dispatchers.IO) { ... } switches the coroutine to a different dispatcher for the block, then returns to the original context. It is the idiomatic way to move work off the main thread:
val data = withContext(Dispatchers.IO) { api.fetch() }

Q: What does the Job interface represent?
A: A Job is a handle to a launched coroutine. It exposes cancel(), join(), isActive, and isCompleted. A Deferred<T> extends Job and adds await() for the result.

Q: What is the lifecycle of a coroutine in a viewModelScope?
A: viewModelScope is tied to the ViewModel's onCleared(). When the ViewModel is destroyed, all coroutines launched in this scope are cancelled automatically — preventing leaks and wasted work.

Q: What does the SupervisorJob allow?
A: A SupervisorJob as the parent prevents child failures from cancelling siblings or the parent. This is the default for viewModelScope and GlobalScope in many setups. Use it for fire-and-forget tasks where one failure should not bring down the others.

Q: What is the difference between Job.cancel() and CancellationException?
A: cancel() sends a cancellation signal — the coroutine is cooperatively cancelled at the next suspension point. It throws CancellationException internally, which is treated as normal completion by coroutine machinery. To honor cancellation, suspending functions should call ensureActive() or other cancellable APIs.

Q: What is a NonCancellable context used for?
A: withContext(NonCancellable) { ... } runs a block where cancellation is ignored (no cancel signal will interrupt it). Use sparingly — typically for cleanup work that must finish, like closing a resource.

Q: What is the purpose of Dispatchers.withName?
A: Dispatchers.IO and Dispatchers.Default are configured with withName for debugging. The name appears in coroutine stack traces and thread dumps, making it easier to see where work is happening.

Q: How does Flow differ from a suspend function?
A: A suspend fun returns a single value. A Flow emits multiple values over time, is cold (no work until collected), and supports operators like map, filter, collect, combine, and debounce.

Q: What is a cold flow vs a hot flow?
A: - Cold (flow { ... }): the producer runs only when a collector subscribes; each collector gets its own stream.
- Hot (StateFlow, SharedFlow, Channel): emits whether or not anyone is collecting; multiple subscribers share or compete for values.