Try-with-resources en Kotlin: cómo deshacerse de finally para siempre

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué los programadores de Java lloran cuando se pasan a Kotlin?

\n\n

Imagine: usted escribe código en Java, abre un archivo, lee datos y luego… se olvida de cerrarlo. ¿Un dolor familiar? En Java, para esto se inventó la construcción try-with-resources, que llama automáticamente a close(). Pero cuando los desarrolladores se pasan a Kotlin, a menudo se quedan perplejos: «¿Dónde está mi querido try (Resource res = ...)?».

\n\n

Spoiler: en Kotlin no existe try-with-resources como una construcción de lenguaje separada. Y esto es una buena noticia. En su lugar, el lenguaje ofrece una solución más elegante y concisa: la función use. Analicemos cómo funciona y por qué su código será más limpio.

\n\n

El problema: los recursos deben cerrarse

\n\n

Cualquier recurso externo (archivo, conexión de red, base de datos) requiere una gestión manual. Si abre un FileInputStream y no lo cierra, el sistema puede «perder memoria» (fuga de memoria) y el archivo permanecerá bloqueado para otros procesos.

\n\n

En Java, este problema se resuelve de dos maneras:

\n\n
    \n
  • La forma antigua: bloque finally con una llamada explícita a close().
  • \n
  • La forma nueva: try-with-resources (apareció en Java 7).
  • \n
\n\n

Kotlin fue más allá: utiliza funciones de extensión y lambdas para hacer que el código no solo sea seguro, sino también hermoso.

\n\n

¿Cómo se ve try-with-resources en Java?

\n\n

Para comparar, veamos el código Java clásico:

\n\n
try (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 cierra automáticamente\n
\n\n

Todo está bien, pero hay matices:

\n
    \n
  • Es necesario declarar la variable dentro de los paréntesis de try.
  • \n
  • Solo se admiten clases que implementan AutoCloseable.
  • \n
  • El código se vuelve voluminoso si hay varios recursos.
  • \n
\n\n

El enfoque de Kotlin: la función use

\n\n

En Kotlin, para trabajar de forma segura con recursos se utiliza la función use. Está definida como una extensión para la interfaz Closeable (y AutoCloseable para compatibilidad con Java).

\n\n

Así es como se ve el mismo ejemplo en Kotlin:

\n\n
import 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 cierra automáticamente\n}\n
\n\n

¿Limpio, conciso, sin try-catch innecesarios? No del todo. use por sí mismo no captura excepciones. Solo garantiza la llamada a close() al final del bloque. Si necesita manejar un error, agregue try-catch externamente:

\n\n
try {\n    BufferedReader(FileReader("file.txt")).use { reader ->\n        val line = reader.readLine()\n        println(line)\n    }\n} catch (e: IOException) {\n    println("Error al leer el archivo: ${e.message}")\n}\n
\n\n

¿Cómo funciona use por debajo?

\n\n

Para entender la magia, echemos un vistazo al código fuente de Kotlin. La función use está definida en la biblioteca estándar más o menos así:

\n\n
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                // suprime la excepción, la añade a la principal\n                exception.addSuppressed(closeExcep                

Blogs

Book Recommendations