Kotlin 2.3 for Android: Name-Based Destructuring, KMP and Interview Questions 2026

Kotlin 2.3 interview questions covering name-based destructuring, Kotlin Multiplatform, context parameters, coroutines and Flow. Prepare for Android developer interviews in 2026 with real-world code examples.

Kotlin 2.3 Android interview questions with destructuring and KMP concepts

Kotlin 2.3 interview questions now rank among the most searched topics for Android developer roles in 2026. With name-based destructuring declarations, stabilized context parameters, and a mature Kotlin Multiplatform ecosystem, interviewers expect candidates to demonstrate fluency in these features.

What changed in Kotlin 2.3

Kotlin 2.3.20, released March 2026, introduces name-based destructuring declarations, overload resolution changes for context parameters, and improved KMP interop with C and TypeScript. These features appear frequently in senior Android interviews.

Name-Based Destructuring: The Most Common Kotlin 2.3 Interview Question

Before Kotlin 2.3, destructuring declarations relied on position-based componentN() functions. This created a well-known pitfall: swapping property order in a data class silently broke existing destructuring sites.

The classic interview question presents this scenario:

User.ktkotlin
data class User(val username: String, val email: String)

fun main() {
    val user = User("alice", "alice@example.com")
    // Position-based: order matters, not names
    val (email, username) = user
    println(email)    // Prints "alice" -- wrong!
    println(username) // Prints "alice@example.com" -- wrong!
}

Kotlin 2.3.20 solves this with name-based destructuring. Three compiler modes control the behavior:

build.gradle.ktskotlin
kotlin {
    compilerOptions {
        // Mode 1: Explicit syntax only
        freeCompilerArgs.add("-Xname-based-destructuring=only-syntax")
        // Mode 2: Warns on name mismatch
        // freeCompilerArgs.add("-Xname-based-destructuring=name-mismatch")
        // Mode 3: Full name-based by default
        // freeCompilerArgs.add("-Xname-based-destructuring=complete")
    }
}

With complete mode, parentheses use name-based matching, and square brackets preserve position-based behavior:

NameBasedDestructuring.ktkotlin
data class User(val username: String, val email: String)

fun main() {
    val user = User("alice", "alice@example.com")

    // Name-based: matches by property name
    val (email, username) = user
    println(email)    // "alice@example.com" -- correct
    println(username) // "alice" -- correct

    // Position-based: square brackets preserve old behavior
    val [first, second] = user
    println(first)  // "alice" (component1)
    println(second) // "alice@example.com" (component2)
}

A strong answer in an interview explains all three modes and identifies when each applies: only-syntax for gradual migration, name-mismatch for catching bugs in existing code, and complete for new projects.

Kotlin Multiplatform Interview Questions for Android Developers

KMP has moved past the experimental phase. Google officially supports it for sharing business logic between Android and iOS, and most new multi-platform projects adopt a shared-core architecture by default.

A typical interview question asks: "How would you structure a KMP module for shared networking?"

shared/src/commonMain/kotlin/com/app/network/ApiClient.ktkotlin
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

@Serializable
data class UserProfile(
    val id: String,
    val displayName: String,
    val avatarUrl: String?
)

// Expect/actual pattern for platform-specific HTTP engine
expect fun createHttpClient(): HttpClient

class ApiClient {
    private val client = createHttpClient()
    private val json = Json { ignoreUnknownKeys = true }

    // Suspend function shared across Android and iOS
    suspend fun fetchProfile(userId: String): UserProfile {
        val response = client.get("https://api.example.com/users/$userId")
        return json.decodeFromString(response.bodyAsText())
    }
}
shared/src/androidMain/kotlin/com/app/network/HttpClient.android.ktkotlin
import io.ktor.client.*
import io.ktor.client.engine.okhttp.*

// Android uses OkHttp engine
actual fun createHttpClient(): HttpClient = HttpClient(OkHttp) {
    engine {
        config {
            retryOnConnectionFailure(true)
        }
    }
}

Interviewers evaluate whether the candidate understands the expect/actual mechanism, platform-specific dependencies, and the boundary between shared and platform code.

Another frequent question covers Swift Export, which has evolved significantly in 2026. Kotlin suspend functions now map directly to Swift async/await, and sealed classes map to Swift enums with associated values.

Context Parameters and Overload Resolution Changes

Context parameters replace the deprecated context receivers API. Kotlin 2.3.20 introduced a breaking change in overload resolution: declarations with context parameters are no longer treated as more specific than those without.

ContextParameters.ktkotlin
class Logger {
    fun info(msg: String) = println("INFO: $msg")
}

class TransactionScope {
    fun execute(block: () -> Unit) = block()
}

// Function with context parameter
context(logger: Logger)
fun saveUser(id: Int) {
    logger.info("Saving user $id")
}

// Overload without context -- now ambiguous in 2.3.20
fun saveUser(id: Int) {
    println("Saving user $id (no logger)")
}

fun main() {
    val logger = Logger()
    // This now produces an ambiguity error in Kotlin 2.3.20
    // context(logger) { saveUser(1) }

    // Fix: rename or qualify the context-aware variant
}

Interviewers use this topic to assess awareness of breaking changes and migration strategies. The key takeaway: relying on implicit context parameter resolution to shadow non-contextual overloads no longer works.

Coroutines and Flow: Still the Top Android Interview Topic

Despite Kotlin 2.3 introducing new features, coroutines and Flow remain the most tested area. Interview questions typically probe the difference between cold and hot flows, structured concurrency, and cancellation.

FlowComparison.ktkotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// Cold Flow: emits only when collected
fun userUpdates(): Flow<String> = flow {
    println("Flow started") // Runs per collector
    emit("User logged in")
    delay(1000)
    emit("User updated profile")
}

// Hot Flow: emits independently of collectors
class UserRepository {
    // StateFlow holds the latest value
    private val _state = MutableStateFlow("idle")
    val state: StateFlow<String> = _state.asStateFlow()

    // SharedFlow broadcasts to multiple collectors
    private val _events = MutableSharedFlow<String>()
    val events: SharedFlow<String> = _events.asSharedFlow()

    suspend fun login() {
        _state.value = "loading"
        delay(500)
        _state.value = "authenticated"
        _events.emit("login_success")
    }
}

A follow-up question often asks about collecting flows safely in Android:

UserViewModel.ktkotlin
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch

class UserViewModel(private val repo: UserRepository) : ViewModel() {

    // Expose state to Compose UI
    val uiState: StateFlow<String> = repo.state
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = "idle"
        )

    init {
        // Collect events safely within ViewModel scope
        viewModelScope.launch {
            repo.events.collect { event ->
                // Handle one-time events
                println("Event received: $event")
            }
        }
    }
}

The WhileSubscribed(5000) strategy keeps the upstream active for 5 seconds after the last subscriber disappears, preventing unnecessary restarts during configuration changes like screen rotation.

Ready to ace your Android interviews?

Practice with our interactive simulators, flashcards, and technical tests.

Sealed Classes and Exhaustive When: Pattern Matching in Interviews

Sealed classes appear in nearly every Android interview. The question typically asks candidates to model a UI state:

UiState.ktkotlin
sealed interface UiState<out T> {
    data object Loading : UiState<Nothing>
    data class Success<T>(val data: T) : UiState<T>
    data class Error(val message: String, val retry: (() -> Unit)? = null) : UiState<Nothing>
}

// Usage in ViewModel
fun loadUsers(): Flow<UiState<List<UserProfile>>> = flow {
    emit(UiState.Loading)
    try {
        val users = apiClient.fetchUsers()
        emit(UiState.Success(users))
    } catch (e: Exception) {
        emit(UiState.Error(
            message = e.localizedMessage ?: "Unknown error",
            retry = { /* trigger reload */ }
        ))
    }
}

In Kotlin, when expressions over sealed types are exhaustive at compile time. Adding a new subtype forces handling it everywhere, which eliminates an entire category of runtime bugs. Interviewers specifically check that the candidate uses sealed interface (not sealed class) for this pattern, since interfaces allow more flexible hierarchies.

Kotlin/Wasm and Cross-Platform Compilation Targets

Kotlin/Wasm reached Beta status and brings near-native performance in browsers. While not yet a daily concern for Android developers, interviewers at companies with web+mobile products ask about it.

Key facts for the interview:

  • Kotlin 2.3.20 delivered 4.6x faster string interpolation in Wasm through JS String builtins
  • Binary sizes dropped approximately 5%
  • Clean builds run 65% faster, incremental builds 21% faster
  • The @nativeInvoke annotation allows Kotlin objects to act as JavaScript functions

Kotlin/JS also gained the ability to implement Kotlin interfaces from TypeScript, removing a previous limitation. Combined with SWC support for transpilation, Kotlin now covers Android, iOS (via KMP), Web (via Wasm/JS), and server-side targets from a single codebase.

Scope Functions: A Deceptively Simple Interview Filter

Scope functions (let, run, with, apply, also) appear simple but differentiate experienced Kotlin developers. The interview question usually asks: "When do you use let versus apply?"

ScopeFunctions.ktkotlin
data class Request(
    var url: String = "",
    var method: String = "GET",
    var headers: MutableMap<String, String> = mutableMapOf(),
    var body: String? = null
)

fun buildRequest(): Request {
    // apply: configure an object, returns the object
    return Request().apply {
        url = "https://api.example.com/users"
        method = "POST"
        headers["Content-Type"] = "application/json"
        body = "{\"name\": \"Alice\"}"
    }
}

fun processNullable(input: String?) {
    // let: transform nullable, returns lambda result
    val length = input?.let { value ->
        println("Processing: $value")
        value.trim().length
    } ?: 0

    // also: side effects, returns the original object
    input?.also { println("Logging input: $it") }

    // run: execute a block with receiver, returns lambda result
    val result = input?.run {
        // 'this' is the string
        uppercase().take(10)
    }
}

The decision framework: apply for object configuration (returns receiver), let for null-safe transformations (returns lambda result), also for side effects (returns receiver), run for computing a result with receiver context.

Null Safety and the Danger of Double-Bang

Null safety remains a fundamental interview topic. The question "Why is !! dangerous?" tests whether a candidate truly understands Kotlin's type system.

NullSafety.ktkotlin
fun findUser(id: String): User? {
    // Returns null if user not found
    return database.queryUser(id)
}

// Bad: !! defeats the purpose of null safety
fun riskyApproach(id: String) {
    val user = findUser(id)!! // NullPointerException if null
    println(user.username)
}

// Good: handle null explicitly
fun safeApproach(id: String) {
    val user = findUser(id) ?: run {
        println("User not found")
        return
    }
    println(user.username)
}

// Good: provide default with Elvis operator
fun displayName(id: String): String {
    return findUser(id)?.username ?: "Anonymous"
}

A complete answer mentions that !! should only appear in test code or platform interop boundaries where null is genuinely impossible.

Start practicing!

Test your knowledge with our interview simulators and technical tests.

Conclusion

  • Name-based destructuring in Kotlin 2.3.20 eliminates position-dependent bugs. Know all three compiler modes: only-syntax, name-mismatch, complete
  • KMP is production-stable in 2026. Expect questions on expect/actual, shared module architecture, and Swift Export interop
  • Context parameters replaced context receivers. The overload resolution change in 2.3.20 is a common trick question
  • Coroutines and Flow dominate Android interviews. Master StateFlow vs SharedFlow, WhileSubscribed, and structured concurrency
  • Sealed interfaces model UI state with compile-time exhaustiveness. Prefer sealed interface over sealed class
  • Scope functions require a clear decision framework: receiver vs argument, return value vs receiver
  • Null safety is non-negotiable. Articulate why !! undermines the type system and demonstrate alternatives

Start practicing!

Test your knowledge with our interview simulators and technical tests.

Tags

#kotlin
#android
#interview
#kmp
#kotlin-multiplatform
#coroutines

Share

Related articles