Pourquoi les développeurs Java pleurent-ils en passant à Kotlin ?
\n\nImaginez : vous écrivez du code en Java, vous ouvrez un fichier, vous lisez les données, et ensuite… vous oubliez de le fermer. Une douleur familière ? En Java, la construction try-with-resources a été créée pour cela, elle appelle automatiquement close(). Mais lorsque les développeurs passent à Kotlin, ils sont souvent perplexes : « Où est mon cher try (Resource res = ...) ? ».
Spoiler : Kotlin n'a pas de try-with-resources en tant que construction linguistique distincte. Et c'est une bonne nouvelle. À la place, le langage propose une solution plus élégante et concise — la fonction use. Voyons comment cela fonctionne et pourquoi votre code deviendra plus propre.
Le problème : les ressources doivent être fermées
\n\nToute ressource externe — fichier, connexion réseau, base de données — nécessite une gestion manuelle. Si vous ouvrez un FileInputStream et ne le fermez pas, le système peut « fuir » (fuite mémoire), et le fichier restera verrouillé pour les autres processus.
En Java, ce problème est résolu de deux manières :
\n\n- \n
- Ancienne méthode : le bloc
finallyavec un appel explicite àclose(). \n - Nouvelle méthode :
try-with-resources(apparu dans Java 7). \n
Kotlin est allé plus loin : il utilise des fonctions d'extension et des lambdas pour rendre le code non seulement sûr, mais aussi élégant.
\n\nÀ quoi ressemble try-with-resources en Java ?
\n\nPour comparaison, regardons le code Java classique :
\n\ntry (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {\n String line = br.readLine();\n System.out.println(line);\n} catch (IOException e) {\n e.printStackTrace();\n}\n// br se ferme automatiquement\n\n\nTout va bien, mais il y a des nuances :
\n- \n
- Il faut déclarer la variable entre les parenthèses du
try. \n - Seules les classes implémentant
AutoCloseablesont prises en charge. \n - Le code devient lourd s'il y a plusieurs ressources. \n
L'approche Kotlin : la fonction use
\n\nEn Kotlin, pour travailler en toute sécurité avec les ressources, on utilise la fonction use. Elle est définie comme une extension de l'interface Closeable (et AutoCloseable pour la compatibilité avec Java).
Voici à quoi ressemble le même exemple en Kotlin :
\n\nimport java.io.BufferedReader\nimport java.io.FileReader\n\nfun main() {\n BufferedReader(FileReader("file.txt")).use { reader ->\n val line = reader.readLine()\n println(line)\n }\n // reader se ferme automatiquement\n}\n\n\nPropre, concis, sans try-catch superflu ? Pas tout à fait. use en lui-même n'attrape pas les exceptions. Il garantit seulement l'appel à close() à la fin du bloc. Si vous devez gérer une erreur, ajoutez un try-catch à l'extérieur :
try {\n BufferedReader(FileReader("file.txt")).use { reader ->\n val line = reader.readLine()\n println(line)\n }\n} catch (e: IOException) {\n println("Erreur lors de la lecture du fichier : ${e.message}")\n}\n\n\nComment fonctionne use sous le capot ?
\n\nPour comprendre la magie, regardons le code source de Kotlin. La fonction use est définie dans la bibliothèque standard à peu près comme ceci :
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {\n var exception: Throwable? = null\n try {\n return block(this)\n } catch (e: Throwable) {\n exception = e\n throw e\n } finally {\n when {\n exception == null -> close()\n else -> try {\n close()\n } catch (closeException: Throwable) {\n // supprime l'exception, l'ajoute à l'exception principale\n exception.addSuppressed(closeException)