DEV Community

Maximiliano Burgos
Maximiliano Burgos

Posted on

Curso Kotlin | #15. Excepciones

Bienvenido/a a otro capítulo del Curso de Kotlin! Podés consultar el curso completo desde este link que te dejo acá. Podés seguirme por LinkedIn o Twitter si querés estar al tanto de las próximas publicaciones.

Las excepciones en Kotlin nos permiten evitar comportamientos no deseados ante problemas que se presentan en tiempo de ejecución. Por ejemplo cuando se intenta dividir por cero, castear un tipo por otro no válido, intentar agregar un valor a una lista nula, entre otros casos. Hoy vamos a ver su implementación en el código.

Manos a la obra

Usemos un ejemplo sencillo de calificaciones: si te doy más de 5 puntos estas aprobado; caso contrario estas reprobado:

print("Cual es tu nota final? > ")
val eval = readLine()!!

if(eval.toInt() >= 6){
    println("Estas aprobado")
} else {
    println("Has reprobado")
}
Enter fullscreen mode Exit fullscreen mode

Si no entiendes alguna parte, deberías pasarte por la clase de condicionales. En caso contrario, verás que lidiamos con un ejemplo muy sencillo, el cual arroja un resultado según la condicion del número. En la variable eval utilizé el bang bang operator (!!) asumiendo que no mi variable será nula y simplificando la parte de la expresión en nuestra condición.

Cual es tu nota final? > 5
Has reprobado
Enter fullscreen mode Exit fullscreen mode

Pero, ¿qué ocurre si en vez de un número, escribo una palabra?:

Cual es tu nota final? > mostaza
Exception in thread "main" java.lang.NumberFormatException: For input string: "mostaza"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at MainKt.main(main.kt:8)
Enter fullscreen mode Exit fullscreen mode

Dado que el mundo todavía no esta preparado para calificar a la gente con aderezos, recibimos un NumberFormatException, la cual indica que no puede convertir mostaza a un número entero.

Bloque Try Catch

No queremos que nuestro programa explote cuando ocurra una excepción: necesitamos capturarla y continuar el flujo normalmente. Para ello existe el bloque Try Catch, y lo implementaremos de la siguiente manera:

try {
    if(eval.toInt() >= 6){
        println("Estas aprobado")
    } else {
        println("Has reprobado")
    }
} catch (e: Exception) {
    println("$eval no es un número valido :(")
}
Enter fullscreen mode Exit fullscreen mode

Dentro del cuerpo de try debemos ingresar las lineas que serán evaluadas y podrían tener una potencial excepción. Aquí es donde muchos desarrolladores cometen errores, dado que escriben medio programa dentro del bloque, lo cual es innecesario. Solo debemos ingresar lo que consideremos que podría generar una excepción. Luego en catch debemos especificar al programa que ocurrirá cuando se presente la excepción. El parámetro e de tipo Exception (clase padre de todas las excepciones) contiene el stack que vimos arriba, entre otra información útil que podremos devolver si fuera necesario. Nosotros solo indicaremos un mensaje de error:

Cual es tu nota final? > mostaza
mostaza no es un número valido :(
Enter fullscreen mode Exit fullscreen mode

Esto funcionará bien siempre con “mostaza”, pero imaginemos que luego de obtener la calificación, la dividiremos por cero, porque estamos dementes:

println("Hora de dividir por cero! :D")
val res = eval.toInt() / 0
Enter fullscreen mode Exit fullscreen mode

Este bloque de código irá al final de nuestro try.

Cual es tu nota final? > 6
Estas aprobado
Hora de dividir por cero! :D
6 no es un número valido :(
Enter fullscreen mode Exit fullscreen mode

Como vemos, el comportamiento de nuestro programa es extraño: primero nos explica que estamos aprobados, pero luego dice que “6” no es un número válido. Esto ocurre porque no estamos distinguiendo entre excepciones. Por suerte, podemos especificarlo de la siguiente manera:

print("Cual es tu nota final? > ")
val eval = readLine()!!

try {
    if(eval.toInt() >= 6){
        println("Estas aprobado")
    } else {
        println("Has reprobado")
    }

    println("Hora de dividir por cero! :D")
    val res = eval.toInt() / 0
} catch (e: NumberFormatException) {
    println("$eval no es un número valido :(")
} catch (e: ArithmeticException) {
    println("$eval no se puede dividir por cero!")
}
Enter fullscreen mode Exit fullscreen mode

Podemos utilizar todos los catch que necesitemos, siempre y cuando nos enfoquemos en las excepciones que queramos evaluar:

Cual es tu nota final? > 6
Estas aprobado
Hora de dividir por cero! :D
6 no se puede dividir por cero!
Enter fullscreen mode Exit fullscreen mode

Recordemos que las excepciones se disparan en el momento en que se ejecuta la linea correspondiente dentro del try y luego salta al catch. Esto funcionaría como un break, por lo cual todo lo que haya debajo no se ejecuta. El disparador en este caso sería eval.toInt() / 0, pero si escribimos un texto:

Cual es tu nota final? > patata
patata no es un número valido :(
Enter fullscreen mode Exit fullscreen mode

La excepción se va a disparar en if(eval.toInt() >= 6){. Esto ocurre antes de dividir por cero, porque nuestro número no es válido.

A romper cosas

Capturar excepciones es importante para que nuestro programa no nos explote en la cara, pero también podemos romper cosas nosotros. Esto será muy útil cuando desarrollemos librerías y queramos arrojar nuestras propias excepciones en la cara de otros desarrolladores.

Imaginemos que creamos un programa que requiere un nombre, pero si llega vacío, lanzaremos un error:

print("Cual es tu nombre? >")
val name = readLine()

if(name.isNullOrEmpty()){
    throw Exception("El nombre no puede estar vacío!")
} else {
    print("Hola $name!")
}
Enter fullscreen mode Exit fullscreen mode

El método isNullOrEmpty pregunta si la variable es nula o esta vacía. Esto solo podemos utilizarlo con los tipo nullable. Si corremos el programa y no introducimos un nombre, nos devolverá lo siguiente:

Cual es tu nombre? >
Exception in thread "main" java.lang.Exception: El nombre no puede estar vacío!
    at MainKt.main(main.kt:12)
Enter fullscreen mode Exit fullscreen mode

Con la sentencia throw y el tipo de excepción, hemos logrado devolver un mensaje claro y detallado del error. Algo curioso es que también nos indica la linea donde se disparó la excepción (12 en mi caso).

Conclusiones

Voy a donarles un pequeño secreto a voces en el mundo del desarrollo: La verdadera diferencia entre un desarrollador junior y uno más experimentado, es el manejo inteligente de excepciones. Si se centran en evitar que sus programas exploten y controlar el momento en el que sea importante lanzar una, van a tener un buen dominio del flujo de ejecución de sus aplicaciones. Este tema se extenderá en las siguientes clases, especialmente cuando implementemos funciones y el paradigma de la programación orientada a objetos.

Oldest comments (0)