Kotlin 2.3 dla Androida: dekonstrukcja po nazwach, KMP i pytania rekrutacyjne 2026
Pytania rekrutacyjne z Kotlin 2.3: dekonstrukcja po nazwach, Kotlin Multiplatform, parametry kontekstowe, korutyny i Flow. Przygotowanie do rozmów Android developera w 2026 z praktycznymi przykładami kodu.

Pytania rekrutacyjne dotyczące Kotlin 2.3 znalazły się w 2026 roku wśród najczęściej wyszukiwanych tematów dla stanowisk Android developera. Wraz z deklaracjami dekonstrukcji opartymi na nazwach, ustabilizowanymi parametrami kontekstowymi oraz dojrzałym ekosystemem Kotlin Multiplatform, rekruterzy oczekują od kandydatów biegłej znajomości tych funkcji.
Kotlin 2.3.20, wydany w marcu 2026, wprowadza deklaracje dekonstrukcji oparte na nazwach, zmiany w rozwiązywaniu przeciążeń dla parametrów kontekstowych oraz ulepszoną interoperacyjność KMP z C i TypeScript. Te funkcje pojawiają się często w rozmowach na stanowiska senior Android.
Dekonstrukcja oparta na nazwach: najczęstsze pytanie rekrutacyjne z Kotlin 2.3
Przed wersją Kotlin 2.3 deklaracje dekonstrukcji opierały się na funkcjach componentN() zależnych od pozycji. Tworzyło to dobrze znaną pułapkę: zamiana kolejności właściwości w data class po cichu psuła istniejące miejsca dekonstrukcji.
Klasyczne pytanie rekrutacyjne przedstawia ten scenariusz:
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 rozwiązuje ten problem dzięki dekonstrukcji opartej na nazwach. Trzy tryby kompilatora kontrolują to zachowanie:
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")
}
}W trybie complete nawiasy okrągłe używają dopasowania po nazwach, a nawiasy kwadratowe zachowują zachowanie oparte na pozycji:
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)
}Mocna odpowiedź podczas rozmowy wyjaśnia wszystkie trzy tryby i wskazuje, kiedy każdy z nich ma zastosowanie: only-syntax dla stopniowej migracji, name-mismatch do wychwytywania błędów w istniejącym kodzie oraz complete dla nowych projektów.
Pytania rekrutacyjne o Kotlin Multiplatform dla Android developerów
KMP wyszedł poza fazę eksperymentalną. Google oficjalnie wspiera go w dzieleniu logiki biznesowej między Androidem a iOS, a większość nowych projektów multi-platform domyślnie przyjmuje architekturę współdzielonego rdzenia.
Typowe pytanie rekrutacyjne brzmi: „Jak ustrukturyzowałbyś moduł KMP dla wspólnej sieci?"
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())
}
}import io.ktor.client.*
import io.ktor.client.engine.okhttp.*
// Android uses OkHttp engine
actual fun createHttpClient(): HttpClient = HttpClient(OkHttp) {
engine {
config {
retryOnConnectionFailure(true)
}
}
}Rekruterzy oceniają, czy kandydat rozumie mechanizm expect/actual, zależności specyficzne dla platform oraz granicę między kodem wspólnym a kodem platformy.
Kolejne częste pytanie dotyczy Swift Export, który znacznie ewoluował w 2026 roku. Funkcje suspend w Kotlinie mapują się teraz bezpośrednio na Swift async/await, a klasy zapieczętowane mapują się na enum Swift z wartościami powiązanymi.
Parametry kontekstowe i zmiany w rozwiązywaniu przeciążeń
Parametry kontekstowe zastępują przestarzałe API context receivers. Kotlin 2.3.20 wprowadził istotną zmianę w rozwiązywaniu przeciążeń: deklaracje z parametrami kontekstowymi nie są już traktowane jako bardziej szczegółowe niż te bez nich.
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
}Rekruterzy używają tego tematu, aby ocenić świadomość przełomowych zmian i strategii migracji. Kluczowy wniosek: poleganie na niejawnym rozwiązywaniu parametrów kontekstowych w celu przesłonięcia przeciążeń bez kontekstu już nie działa.
Korutyny i Flow: nadal najważniejszy temat rozmów Android
Pomimo nowych funkcji wprowadzonych w Kotlin 2.3, korutyny i Flow pozostają najczęściej testowanym obszarem. Pytania rekrutacyjne zwykle dotyczą różnicy między zimnymi a gorącymi flow, strukturalnej współbieżności oraz anulowania.
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")
}
}Uzupełniające pytanie często dotyczy bezpiecznego zbierania flow na Androidzie:
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")
}
}
}
}Strategia WhileSubscribed(5000) utrzymuje upstream aktywny przez 5 sekund po zniknięciu ostatniego subskrybenta, zapobiegając niepotrzebnym restartom podczas zmian konfiguracji, takich jak obrót ekranu.
Gotowy na rozmowy o Android?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Sealed Classes i wyczerpujące when: pattern matching podczas rozmów
Klasy zapieczętowane pojawiają się w niemal każdej rozmowie Android. Pytanie zwykle prosi kandydatów o zamodelowanie stanu interfejsu użytkownika:
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 */ }
))
}
}W Kotlinie wyrażenia when na typach zapieczętowanych są wyczerpujące w czasie kompilacji. Dodanie nowego podtypu wymusza jego obsługę wszędzie, co eliminuje całą kategorię błędów wykonania. Rekruterzy sprawdzają konkretnie, czy kandydat używa sealed interface (a nie sealed class) dla tego wzorca, ponieważ interfejsy pozwalają na bardziej elastyczne hierarchie.
Kotlin/Wasm i międzyplatformowe cele kompilacji
Kotlin/Wasm osiągnął status Beta i zapewnia wydajność zbliżoną do natywnej w przeglądarkach. Choć nie jest to jeszcze codzienna troska Android developerów, rekruterzy w firmach z produktami webowymi i mobilnymi pytają o niego.
Kluczowe fakty na rozmowę:
- Kotlin 2.3.20 dostarczył 4,6 razy szybszą interpolację stringów w Wasm dzięki JS String builtins
- Rozmiary plików binarnych zmalały o około 5%
- Clean buildy działają o 65% szybciej, buildy inkrementalne o 21% szybciej
- Adnotacja
@nativeInvokepozwala obiektom Kotlin działać jako funkcje JavaScript
Kotlin/JS zyskał również możliwość implementowania interfejsów Kotlin z TypeScript, usuwając wcześniejsze ograniczenie. W połączeniu ze wsparciem SWC dla transpilacji, Kotlin pokrywa teraz Androida, iOS (przez KMP), Web (przez Wasm/JS) i cele serwerowe z jednej bazy kodu.
Funkcje scope: pozornie proste pytanie filtrujące podczas rozmów
Funkcje scope (let, run, with, apply, also) wydają się proste, ale różnicują doświadczonych deweloperów Kotlin. Pytanie rekrutacyjne zwykle brzmi: „Kiedy używać let zamiast apply?"
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)
}
}Framework decyzyjny: apply do konfiguracji obiektu (zwraca odbiorcę), let do przekształceń bezpiecznych dla null (zwraca wynik lambdy), also do efektów ubocznych (zwraca odbiorcę), run do obliczania wyniku w kontekście odbiorcy.
Null safety i niebezpieczeństwo podwójnego wykrzyknika
Null safety pozostaje fundamentalnym tematem rozmów. Pytanie „Dlaczego !! jest niebezpieczne?" sprawdza, czy kandydat naprawdę rozumie system typów Kotlina.
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"
}Kompletna odpowiedź wspomina, że !! powinno pojawiać się tylko w kodzie testowym lub na granicach interop platformy, gdzie null jest naprawdę niemożliwy.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Podsumowanie
- Dekonstrukcja oparta na nazwach w Kotlin 2.3.20 eliminuje błędy zależne od pozycji. Warto znać wszystkie trzy tryby kompilatora:
only-syntax,name-mismatch,complete - KMP jest stabilny produkcyjnie w 2026 roku. Spodziewaj się pytań o expect/actual, architekturę współdzielonego modułu i interop Swift Export
- Parametry kontekstowe zastąpiły context receivers. Zmiana w rozwiązywaniu przeciążeń w 2.3.20 to typowe pytanie podchwytliwe
- Korutyny i Flow dominują w rozmowach Android. Opanuj
StateFlowvsSharedFlow,WhileSubscribedoraz strukturalną współbieżność - Sealed interface modelują stan UI z wyczerpującością na etapie kompilacji. Preferuj
sealed interfacezamiastsealed class - Funkcje scope wymagają jasnego frameworka decyzyjnego: odbiorca vs argument, wartość zwracana vs odbiorca
- Null safety nie podlega negocjacji. Wyjaśnij, dlaczego
!!podważa system typów i pokaż alternatywy
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Tagi
Udostępnij
Powiązane artykuły

20 najczesciej zadawanych pytan rekrutacyjnych z Jetpack Compose w 2026
20 najczesciej zadawanych pytan na rozmowie kwalifikacyjnej z Jetpack Compose: rekompozycja, zarzadzanie stanem, nawigacja, wydajnosc i wzorce architektoniczne.

Kotlin Coroutines na Androida: Kompletny Przewodnik 2026
Kompleksowy przewodnik po korutynach Kotlin w programowaniu Android: funkcje suspend, zakresy, dispatchery, Flow i zaawansowane wzorce.

Wstrzykiwanie zależności na Androidzie: Hilt vs Koin - kompletny przewodnik z pytaniami rekrutacyjnymi 2026
Szczegółowe porównanie Hilt 2.57 i Koin 4.2 dla Android DI: walidacja w czasie kompilacji vs runtime, benchmarki wydajności, testowanie, architektura wielomodułowa i pytania rekrutacyjne.