Comment réussir un entretien Kotlin : top questions de niveau avancé
\n\nUn entretien pour un poste de développeur Kotlin ne se limite pas à une vérification de la syntaxe. Au niveau avancé, les recruteurs veulent voir une compréhension approfondie du langage, de ses spécificités et la capacité à écrire un code efficace, concis et sûr. Dans cet article, nous analysons les sujets clés qui reviennent souvent lors des entretiens techniques : les coroutines, les fonctions inline, les classes Sealed, la délégation et les astuces des expressions lambda. Ce contenu est préparé pour le portail éducatif PythonLib, où nous aidons les développeurs à systématiser leurs connaissances.
\n\n1. Coroutines et Structured Concurrency
\nLes coroutines sont la base de la programmation asynchrone en Kotlin. Les questions sur ce sujet montrent si vous comprenez le fonctionnement du planificateur, du contexte et de la gestion des exceptions.
\n\nQu'est-ce que Structured Concurrency et pourquoi est-ce nécessaire ?
\nStructured Concurrency (concurrence structurée) est un principe selon lequel le cycle de vie d'une coroutine est lié à une portée (scope). Si la coroutine parente est annulée, toutes les tâches filles sont automatiquement annulées. Cela évite les fuites de mémoire et rend le code prévisible.
\n\nfun main() = runBlocking {\n launch {\n delay(1000)\n println("Tâche 1 terminée")\n }\n launch {\n delay(500)\n println("Tâche 2 terminée")\n }\n println("Le thread principal n'est pas bloqué")\n}\n\n\nDifférence entre launch et async
\nlaunch lance une coroutine qui ne retourne pas de résultat (Unit). async retourne un Deferred, dont on peut obtenir le résultat via await(). async est pratique pour les calculs parallèles.
val deferred1 = async { calculateSum(10, 20) }\nval deferred2 = async { calculateSum(30, 40) }\nval result = deferred1.await() + deferred2.await()\nprintln("Somme : $result")\n\n\nGestion des exceptions dans les coroutines
\nUtilisez try-catch à l'intérieur de la coroutine ou CoroutineExceptionHandler pour une gestion globale. Il est important de se rappeler que les exceptions dans launch se propagent vers le haut de la hiérarchie, tandis que dans async, elles sont stockées jusqu'à l'appel de await().
val handler = CoroutineExceptionHandler { _, exception ->\n println("Erreur : ${exception.message}")\n}\n\nfun main() = runBlocking {\n val job = launch(handler) {\n throw RuntimeException("Quelque chose s'est mal passé")\n }\n}\n\n\n2. Fonctions inline, reified et crossinline
\nCes mécanismes permettent d'optimiser les performances et de travailler avec les génériques à l'exécution. Les recruteurs demandent souvent la différence entre inline, noinline et crossinline.
Pourquoi les fonctions inline sont-elles nécessaires ?
\nLes fonctions inline substituent le corps de la fonction directement à l'endroit de l'appel. Cela élimine les frais généraux liés à la création d'objets lambda. Particulièrement utile pour les higher-order functions.
\n\ninline fun measureTime(action: () -> Unit) {\n val start = System.currentTimeMillis()\n action()\n println("Temps : ${System.currentTimeMillis() - start} ms")\n}\n\n\nQu'est-ce que reified ?
\nAvec reified, on peut obtenir des informations sur le type à l'intérieur d'une fonction inline, ce qui est impossible pour les génériques classiques en raison de l'effacement des types.
inline fun <reified T> isInstance(value: Any): Boolean {\n return value is T\n}\n\nfun main() {\n println(isInstance<String>("Hello")) // true\n}\n\n\nDifférence entre noinline et crossinline
\n- \n
noinline— interdit l'inlinage d'un lambda spécifique (par exemple, s'il est passé à une autre fonction). \n crossinline— autorise l'inlinage, mais interdit le non-local return (retour non local) depuis le lambda, ce qui est sûr pour les appels asynchrones. \n