# Kotlin 2.3 pour Android : Destructuration par Nom, KMP et Questions d'Entretien 2026 > Questions d'entretien Kotlin 2.3 pour développeurs Android en 2026. Destructuration par nom, KMP, paramètres de contexte, Flow et coroutines avec exemples de code. - Published: 2026-05-06 - Updated: 2026-05-06 - Author: SharpSkill - Tags: kotlin, android, entretien, kmp, coroutines - Reading time: 9 min --- Kotlin 2.3 marque une etape importante dans l'evolution du langage pour le developpement Android. Cette version introduit la destructuration par nom, une fonctionnalite qui elimine une source frequente de bugs subtils dans les bases de code existantes. Les developpeurs Android qui preparent des entretiens techniques en 2026 doivent maitriser ces nouveautes, ainsi que les patterns Kotlin Multiplatform (KMP) et les bonnes pratiques autour des coroutines et des Flows. > **Ce qui change avec Kotlin 2.3** > > La destructuration par nom permet de faire correspondre les variables aux proprietes par leur nom plutot que par leur position. Les parametres de contexte sortent de la phase experimentale avec de nouvelles regles de resolution. Kotlin Multiplatform devient le standard recommande pour le partage de code entre Android et iOS. ## La destructuration par nom : resolution d'un probleme historique Avant Kotlin 2.3, la destructuration des data classes reposait uniquement sur la position des composants. Cette approche, bien que simple, creait des bugs difficiles a detecter lors de la refactorisation du code. ```kotlin // User.kt 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! } ``` Ce code compile sans erreur, mais produit un resultat incorrect. Le developpeur a nomme ses variables `email` et `username`, mais la destructuration ignore ces noms et utilise les positions `component1()` et `component2()`. Ce type de bug survient frequemment lors du renommage ou de la reordonnation des proprietes d'une data class. Kotlin 2.3 introduit trois modes de compilation pour gerer cette transition progressivement. ```kotlin // build.gradle.kts 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") } } ``` Le mode `only-syntax` active uniquement la nouvelle syntaxe explicite. Le mode `name-mismatch` emet des avertissements lorsque les noms de variables ne correspondent pas aux proprietes. Le mode `complete` active la destructuration par nom par defaut. Avec le mode complet active, le meme code produit maintenant le resultat attendu. ```kotlin // NameBasedDestructuring.kt 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) } ``` La nouvelle syntaxe avec crochets `[first, second]` preserve le comportement positionnel pour les cas ou il reste necessaire. Cette distinction claire entre les deux approches elimine toute ambiguite. ## Kotlin Multiplatform : partage de code Android et iOS Kotlin Multiplatform (KMP) atteint un niveau de maturite qui en fait le choix recommande par Google pour partager la logique metier entre Android et iOS. Les entretiens techniques en 2026 evaluent frequemment la comprehension du pattern expect/actual. ```kotlin // shared/src/commonMain/kotlin/com/app/network/ApiClient.kt 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()) } } ``` Ce code reside dans le module `commonMain` et compile pour toutes les plateformes cibles. La declaration `expect` indique qu'une implementation specifique a chaque plateforme doit fournir la fonction `createHttpClient()`. ```kotlin // shared/src/androidMain/kotlin/com/app/network/HttpClient.android.kt import io.ktor.client.* import io.ktor.client.engine.okhttp.* // Android uses OkHttp engine actual fun createHttpClient(): HttpClient = HttpClient(OkHttp) { engine { config { retryOnConnectionFailure(true) } } } ``` L'implementation Android utilise OkHttp, le moteur HTTP standard de la plateforme. Une implementation equivalente pour iOS utiliserait le moteur Darwin. Ce pattern permet de beneficier des optimisations natives de chaque plateforme tout en partageant la logique de serialisation et de gestion des erreurs. ## Parametres de contexte : nouvelles regles en 2.3.20 Les parametres de contexte, introduits experimentalement dans les versions precedentes, evoluent avec de nouvelles regles de resolution des ambiguites dans Kotlin 2.3.20. ```kotlin // ContextParameters.kt 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 } ``` Cette modification vise a rendre le code plus explicite et a eviter les comportements surprenants lors de la resolution des surcharges. Les developpeurs doivent renommer ou qualifier explicitement les variantes contextuelles pour lever l'ambiguite. ## Flow et StateFlow : gestion des flux de donnees reactifs La comprehension des Flows constitue un prerequis pour tout developpeur Android moderne. Les entretiens techniques distinguent systematiquement les Cold Flows des Hot Flows. ```kotlin // FlowComparison.kt import kotlinx.coroutines.* import kotlinx.coroutines.flow.* // Cold Flow: emits only when collected fun userUpdates(): Flow = 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 = _state.asStateFlow() // SharedFlow broadcasts to multiple collectors private val _events = MutableSharedFlow() val events: SharedFlow = _events.asSharedFlow() suspend fun login() { _state.value = "loading" delay(500) _state.value = "authenticated" _events.emit("login_success") } } ``` Un Cold Flow ne s'execute que lorsqu'un collecteur s'y abonne. Chaque collecteur declenche une nouvelle execution du bloc `flow`. Un Hot Flow, en revanche, emet des valeurs independamment des collecteurs. Le `StateFlow` conserve toujours la derniere valeur emise, tandis que le `SharedFlow` peut emettre des evenements ponctuels sans valeur initiale. L'integration avec le ViewModel suit un pattern etabli qui garantit la gestion correcte du cycle de vie. ```kotlin // UserViewModel.kt 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 = 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") } } } } ``` L'operateur `stateIn` convertit un Flow froid en StateFlow chaud. Le parametre `WhileSubscribed(5000)` maintient le Flow actif pendant 5 secondes apres le dernier collecteur, ce qui gere elegamment les rotations d'ecran sans redemarrer le Flow inutilement. ## Modelisation des etats avec sealed interface Les sealed interfaces representent le pattern recommande pour modeliser les etats d'une interface utilisateur. Ce pattern garantit l'exhaustivite des verifications lors de la compilation. ```kotlin // UiState.kt sealed interface UiState { data object Loading : UiState data class Success(val data: T) : UiState data class Error(val message: String, val retry: (() -> Unit)? = null) : UiState } // Usage in ViewModel fun loadUsers(): Flow>> = 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 */ } )) } } ``` L'utilisation de `sealed interface` plutot que `sealed class` permet aux sous-types d'heriter d'autres classes si necessaire. Le modificateur `out` sur le parametre de type T assure la covariance, permettant d'utiliser `UiState` la ou `UiState` est attendu. ## Fonctions de portee : apply, let, also, run Les fonctions de portee Kotlin simplifient les operations courantes sur les objets. Leur maitrise distingue les developpeurs experimentes lors des revues de code et des entretiens. ```kotlin // ScopeFunctions.kt data class Request( var url: String = "", var method: String = "GET", var headers: MutableMap = 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) } } ``` La fonction `apply` configure un objet et le retourne, ideale pour les builders. La fonction `let` transforme une valeur nullable et retourne le resultat de la lambda. La fonction `also` execute des effets de bord tout en retournant l'objet original, utile pour le logging. La fonction `run` execute un bloc avec un recepteur et retourne le resultat. ## Null safety : patterns et anti-patterns La gestion des valeurs nullables constitue un pilier de Kotlin. Les entretiens evaluent la capacite a eviter les patterns dangereux tout en ecrivant du code concis. ```kotlin // NullSafety.kt 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" } ``` L'operateur `!!` contourne le systeme de null safety et devrait etre evite dans la quasi-totalite des cas. L'operateur Elvis `?:` combine avec `run` ou `return` offre une gestion elegante des cas null. Cette approche rend le code plus robuste et son intention plus claire. ## Conclusion Kotlin 2.3 apporte des ameliorations significatives pour le developpement Android moderne. Les points essentiels a retenir pour les entretiens techniques en 2026 incluent : - **Destructuration par nom** : comprendre la difference entre la correspondance par position et par nom, savoir configurer les modes de compilation et utiliser la syntaxe avec crochets pour forcer le comportement positionnel - **Kotlin Multiplatform** : maitriser le pattern expect/actual, structurer les modules commonMain et platformMain, utiliser Ktor pour les appels reseau partages - **Parametres de contexte** : connaitre les nouvelles regles de resolution des ambiguites en 2.3.20 et savoir les contourner - **Flows et coroutines** : distinguer Cold Flow et Hot Flow, utiliser StateFlow pour l'etat et SharedFlow pour les evenements, appliquer correctement `stateIn` avec `WhileSubscribed` - **Sealed interfaces** : modeliser les etats UI de maniere exhaustive, utiliser la covariance avec le modificateur `out` - **Fonctions de portee** : choisir la fonction appropriee selon le contexte (apply pour configuration, let pour transformation, also pour logging, run pour execution) - **Null safety** : eviter l'operateur `!!`, privilegier l'operateur Elvis et les smart casts Ces competences constituent le socle attendu des developpeurs Android seniors et sont systematiquement evaluees lors des processus de recrutement en 2026. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/fr/blog/android/kotlin-2-3-interview-questions