Try-Catch: esencia, construcción de captura de excepciones y cómo funciona en el desarrollo móvil

Autor: IT Sectr Publicado: 2026-05-25 Tiempo de lectura: 9 min

Try-Catch es una construcción de captura de excepciones que permite ejecutar código potencialmente peligroso en un bloque protegido y manejar errores correctamente sin que el programa se cierre inesperadamente. El bloque try contiene código que puede lanzar una excepción, catch la intercepta y ejecuta la lógica de recuperación. Según la Apple Swift Documentation (2026), el bloque finally se ejecuta independientemente de si se lanzó una excepción o no, garantizando la liberación de recursos.

Puntos clave

  • Try-Catch es una construcción para capturar excepciones que evita el cierre inesperado del programa ante errores
  • El bloque try contiene código que puede lanzar una excepción — la ejecución se interrumpe ante el primer error
  • El bloque catch captura la excepción del tipo especificado y ejecuta la lógica de manejo o recuperación
  • El bloque finally se ejecuta garantizadamente después de try/catch para liberar recursos, por ejemplo cerrar archivos
  • Try-catch anidados permiten manejar errores en diferentes niveles de abstracción dentro de una misma función

¿Qué es Try-Catch?

Try-Catch es una construcción fundamental de manejo estructurado de excepciones presente en la mayoría de los lenguajes de programación modernos. Consta de tres bloques: try (intento de ejecución de código peligroso), catch (intercepción y manejo de la excepción) y finally opcional (finalización). La idea de la construcción es separar la lógica de negocio de la lógica de manejo de errores, haciendo el código más legible y predecible.

El concepto se implementó por primera vez en C++ como try/catch, luego fue adoptado por Java, C#, Swift, Kotlin, Dart, Python, JavaScript y otros lenguajes. Cada lenguaje añade sus propias características: en Swift el bloque catch debe ser exhaustivo, en Kotlin try-catch puede ser una expresión, en Dart finally es obligatorio para recursos de flujo. A pesar de las diferencias, el principio básico es el mismo: el error se maneja lo más cerca posible del lugar donde ocurre, no globalmente.

El uso de Try-Catch es especialmente importante en el desarrollo móvil, donde los factores externos — pérdida de red, respuesta incorrecta del servidor, memoria insuficiente — ocurren constantemente. Un manejo adecuado de excepciones evita cierres inesperados de la aplicación y garantiza una UX correcta: el usuario ve un mensaje de error en lugar de un cierre repentino. Según la Google Android Kotlin Style Guide (2026), cada función que pueda lanzar una excepción debe manejarla mediante try-catch o declarar throws en su firma.

Cómo funciona Try-Catch

El mecanismo de ejecución de try-catch se basa en el desenrollado de la pila (stack unwinding). Cuando se lanza una excepción dentro del bloque try mediante el operador throw (o como resultado de un error del sistema), el flujo normal de ejecución se interrumpe inmediatamente. La ejecución se mueve un nivel arriba en la pila de llamadas en busca de un bloque catch adecuado. Los lenguajes modernos buscan un catch cuyo tipo coincida con el tipo de la excepción lanzada mediante un mecanismo de coincidencia de tipos (type matching).

Si se encuentra un catch coincidente, se ejecuta su cuerpo, después de lo cual la ejecución continúa después de toda la construcción try-catch-finally. Si no se encuentra ningún catch, la excepción asciende por la pila y puede ser manejada en un nivel superior — hasta un manejador global que, en una aplicación móvil, muestra al usuario un diálogo de error. Si la excepción no se maneja en ninguna parte, la aplicación se cierra inesperadamente. Por eso el manejo correcto de todos los tipos posibles de excepciones es crítico para la estabilidad de la aplicación.

Sintaxis de la construcción básica

kotlin
fun readUserData(): User {
    return try {
        val response = api.fetchUser()
        parseUser(response)
    } catch (e: IOException) {
        logError("Error de red", e)
        throw AppException("No se pudieron cargar los datos")
    } catch (e: JsonParseException) {
        logError("Error de análisis", e)
        return User.default()
    } finally {
        closeLoadingIndicator()
    }
}

El código primero intenta hacer una solicitud a la API y analizar la respuesta. Si ocurre una IOException (problema de red), la excepción se registra y se relanza como AppException. Si ocurre una JsonParseException — se devuelve un usuario por defecto. El bloque finally garantiza ocultar el indicador de carga, evitando fugas de componentes UI en la pantalla.

Try-Catch en Swift

En Swift, el manejo de errores se implementa mediante el protocolo Error (anteriormente ErrorType). Cualquier tipo que conforme a Error puede lanzarse mediante el operador throw. Una función que puede lanzar un error se marca con la palabra clave throws en su firma. Llamar a dicha función requiere el prefijo try (para try-catch explícito), try? (resultado opcional) o try! (ejecución forzada sin manejo de errores).

swift
enum NetworkError: Error {
    case noConnection
    case serverError(code: Int)
    case timeout
}

func fetchUser(id: Int) throws -> User {
    guard isConnected() else {
        throw NetworkError.noConnection
    }
    let data = try performRequest(path: "/users/\(id)")
    return try decodeUser(from: data)
}

do {
    let user = try fetchUser(id: 42)
    updateUI(user)
} catch NetworkError.noConnection {
    showOfflineAlert()
} catch let error as NetworkError {
    showError("Red " + error.localizedDescription)
} catch {
    showGenericError()
}

El enum NetworkError implementa el protocolo Error, definiendo tres casos: noConnection, serverError con un código y timeout. La función fetchUser está marcada con throws: primero verifica la conexión, luego realiza la solicitud y el análisis. En el bloque do-catch, tres catch manejan diferentes escenarios: el caso específico noConnection, el tipo general NetworkError y todos los demás errores. Esto permite mostrar al usuario diferentes mensajes según el tipo de problema.

Try-Catch en Kotlin

Kotlin heredó try-catch-finally de Java pero añadió una diferencia importante: en Kotlin, try-catch es una expresión (expression), no una instrucción (statement). Esto significa que el resultado de un bloque try o catch puede asignarse a una variable. La última expresión en el bloque try se convierte en el resultado en caso de éxito, la última expresión en el catch — en caso de error. Si el error no es manejado por ningún catch, la excepción asciende por la pila.

kotlin
sealed class Result<out T> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Error(val exception: Throwable) : Result<Nothing>()
}

fun loadData(): Result<List<Item>> {
    return try {
        val response = api.getItems()
        Result.Success(response.toList())
    } catch (e: HttpException) {
        Log.e("HTTP ", e)
        Result.Error(e)
    } catch (e: IOException) {
        Log.e("Red ", e)
        Result.Error(e)
    }
}

En el ejemplo, sealed class Result envuelve una respuesta exitosa o un error. La función loadData usa try-catch como expresión: en caso de éxito devuelve Result.Success, en caso de HttpException o IOException — Result.Error con registro. Este enfoque permite al llamante manejar errores sin excepciones — mediante una expresión when sobre el tipo Result. Esto es especialmente conveniente en Jetpack Compose para mostrar diferentes estados de UI (Loading, Success, Error) mediante StateFlow y collectAsState.

Try-Catch en Dart y Flutter

Dart soporta try-catch-finally con una sintaxis similar a Java, pero con la adición de una cláusula on para filtrar por tipo de excepción sin especificar una variable. Esto es conveniente cuando la excepción en sí no es necesaria — solo importa el hecho de su tipo. Dart también soporta un bloque catch con dos parámetros: el objeto de excepción y StackTrace, útil para registrar la cadena completa de llamadas.

dart
import 'dart:io';
import 'dart:convert';

class UserRepository {
    Future<User> fetchUser(String id) async {
        try {
            final client = HttpClient();
            final request = await client.getUrl(
                Uri.parse('https://api.example.com/users/$id')
            );
            final response = await request.close();
            final body = await response.transform(utf8.decoder).join();
            return User.fromJson(json.decode(body));
        } on SocketException catch (e, stackTrace) {
            log("No internet", e, stackTrace);
            throw AppException("Connection failed");
        } on FormatException {
            throw AppException("Invalid response format");
        } finally {
            client.close();
        }
    }
}

SocketException se captura con el objeto de excepción y StackTrace para un registro detallado, y luego se relanza como AppException. FormatException se captura sin variable — basta con saber que el formato de la respuesta es incorrecto. El bloque finally garantiza cerrar HttpClient, evitando fugas de sockets. En Flutter, este enfoque es especialmente importante para las pruebas de Widget, donde las excepciones no manejadas en State.initState provocan la caída de toda la sesión de prueba.

Errores comunes al usar Try-Catch

Incluso los desarrolladores experimentados cometen errores al trabajar con try-catch que provocan fugas de memoria, errores ocultos o un comportamiento inadecuado de la aplicación. Veamos los cinco problemas más comunes en el desarrollo móvil.

Bloque catch vacío

El catch vacío es una de las peores prácticas. La excepción se traga, la aplicación continúa funcionando en un estado incorrecto y el desarrollador nunca se entera del problema. Siempre al menos registre la excepción. En Kotlin use catch(e: Exception) { Log.e(...) }, en Swift — catch { print($0) }. En Dart, un catch mínimamente aceptable debe llamar a debugPrint o escribir en Crashlytics.

Catch demasiado amplio

Capturar todas las excepciones mediante catch (Exception e) sin distinguir por tipos oculta errores inesperados — NullPointerException, OutOfMemoryError, StackOverflowError. Capture solo los tipos que espera y puede manejar. Para todo lo demás, permita la propagación hacia arriba. En el desarrollo móvil, los catch específicos para IOException, TimeoutException, AuthException dan mensajes más significativos al usuario.

Ignorar finally

Los recursos — archivos, sockets, cursores de BD, animaciones — deben liberarse en finally o en un bloque use (AutoCloseable). Los desarrolladores a menudo olvidan cerrar los recursos cuando ocurre una excepción, lo que provoca fugas. En Kotlin use .use { } para recursos Closeable, en Swift — defer { }, en Dart — await using del paquete async. El bloque finally garantiza la liberación incluso cuando se lanza una excepción dentro de catch.

Excepciones en hilos y corrutinas

En código asíncrono, try-catch no captura excepciones de otros hilos. En Kotlin Coroutines use CoroutineExceptionHandler o SupervisorJob. En Swift async/await — do-catch dentro de Task. En Flutter — runZonedGuarded para captura global. Ignorar esta regla es la causa de cierres inesperados difíciles de reproducir en producción.

Bloqueo de UI ante una excepción

El manejo de excepciones no debe bloquear la interfaz de usuario indefinidamente. Muestre al usuario un mensaje específico y dele la oportunidad de reintentar la operación. Una Snackbar con botón Retry en Kotlin/Compose, UIAlertController con acción en Swift, SnackBar con acción en Flutter — una UX mínimamente suficiente para errores de red o servidor. Evite diálogos genéricos como “Ocurrió un error” sin opciones de recuperación.

Preguntas frecuentes

¿En qué se diferencia try-catch de Result Type?

Try-catch usa excepciones y desenrollado de pila para el manejo de errores, lo que puede ser costoso en rendimiento cuando hay muchos errores. Result Type es un tipo contenedor (Success o Failure) que se maneja mediante pattern matching sin desenrollado de pila, siendo más eficiente para errores esperados.

¿Es necesario usar finally en cada try-catch?

Finally es obligatorio si el bloque try abre recursos (archivos, sockets, cursores) que deben cerrarse. Si no se abren recursos, finally no es necesario. En lenguajes modernos use AutoCloseable/use/defer para el cierre automático de recursos sin finally. El bloque use en Kotlin y Swift reemplaza finally para objetos Closeable.

¿Puede try-catch ser lento?

En el flujo normal (sin excepciones), try-catch prácticamente no afecta el rendimiento — la JVM y el compilador de Swift optimizan este caso. Pero cuando se lanza una excepción, ocurre el desenrollado de pila, que puede tomar 10–100 μs dependiendo de la profundidad de la pila. No use excepciones para el control de flujo — es un antipatrón.

¿Cómo manejar errores en Kotlin Coroutines?

En corrutinas, use try-catch dentro de coroutineScope o CoroutineExceptionHandler para captura global. SupervisorJob evita que la corrutina padre se cancele cuando una corrutina hija falla. Para launch use CoroutineExceptionHandler, para async — try-catch alrededor de await().

¿Qué es mejor: múltiples catch o un solo catch con if-else?

Los múltiples catch son preferibles: el código se lee linealmente, cada bloque maneja un tipo de excepción. Un solo catch con if-else es más difícil de mantener y es fácil pasar por alto un nuevo tipo de excepción. En Swift los múltiples catch son obligatorios para el manejo exhaustivo de enum Error, en Kotlin no hay restricciones, pero la mejor práctica es un catch separado por tipo.

Resumen

  • Try-Catch es una construcción de captura de excepciones con bloques try, catch y finally opcional para liberación garantizada de recursos
  • Mecanismo de funcionamiento — desenrollado de pila al lanzar una excepción con búsqueda del catch adecuado por tipo de error
  • En Swift se usa do-catch con enum Error, try? para resultado opcional y try! para éxito garantizado
  • En Kotlin try-catch es una expresión cuyo resultado puede asignarse a una variable, conveniente con sealed class Result
  • En Dart soporta cláusula on para filtrar por tipo sin variable y finally para cerrar HttpClient
  • Errores comunes: catch vacío, catch demasiado amplio, ignorar finally y falta de manejo en corrutinas
  • Use try-catch para errores inesperados, Result Type — para escenarios esperados con posible fallo

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también