Kotlin

Kotlin

Tutorial Kotlin: Estructuras de control

Aprende estructuras de control en Kotlin, con condicionales como if, else, when, bucles tales como for, while, do-while y manejo de excepciones con try-catch, para mejorar tu código.

Aprende Kotlin GRATIS y certifícate

Estructuras condicionales (if, else, when)

Las estructuras condicionales en Kotlin permiten controlar el flujo de ejecución de un programa en función de ciertas condiciones. Estas estructuras son fundamentales para la toma de decisiones dentro del código. A continuación, se detallan las estructuras if, else y when.

La expresión if en Kotlin se utiliza para evaluar una condición y ejecutar un bloque de código si dicha condición es verdadera. Además, a diferencia de algunos lenguajes, en Kotlin if es una expresión, lo que significa que puede devolver un valor. Por ejemplo:

val numero = 10
val resultado = if (numero > 0) "Positivo" else "No positivo"
println(resultado) // Imprime "Positivo"

En este caso, resultado almacenará el valor que devuelve la expresión if. Si la condición numero > 0 es verdadera, se asigna "Positivo"; de lo contrario, "No positivo".

Cuando se necesita evaluar múltiples condiciones, se pueden encadenar varias expresiones if y else. Esto permite crear una lógica más compleja:

val nota = 85
val calificacion = if (nota >= 90) {
    "Excelente"
} else if (nota >= 70) {
    "Bueno"
} else {
    "Necesita mejorar"
}
println(calificacion) // Imprime "Bueno"

Es importante destacar que, al ser una expresión, el último valor evaluado dentro de cada bloque {} será el que se devuelva.

Por otro lado, la estructura when es una alternativa más limpia y legible a múltiples sentencias if-else. Se utiliza para seleccionar entre diferentes posibles valores o condiciones. Por ejemplo:

val dia = 3
val nombreDia = when (dia) {
    1 -> "Lunes"
    2 -> "Martes"
    3 -> "Miércoles"
    4 -> "Jueves"
    5 -> "Viernes"
    6, 7 -> "Fin de semana"
    else -> "Día no válido"
}
println(nombreDia) // Imprime "Miércoles"

En este ejemplo, when evalúa el valor de dia y coincide con el caso correspondiente. Es posible agrupar múltiples valores en una sola rama, como se hace con 6, 7.

Además, when puede utilizarse sin un argumento, permitiendo evaluar condiciones más complejas:

val numero = -5
val tipoNumero = when {
    numero > 0 -> "Positivo"
    numero < 0 -> "Negativo"
    else -> "Cero"
}
println(tipoNumero) // Imprime "Negativo"

Aquí, cada rama de when es una condición que se evalúa hasta encontrar la que es verdadera.

Es posible también utilizar expresiones de rango con when, lo que facilita comprobar si un valor se encuentra dentro de un intervalo:

val edad = 25
val etapaVida = when (edad) {
    in 0..12 -> "Niñez"
    in 13..19 -> "Adolescencia"
    in 20..64 -> "Adultez"
    else -> "Vejez"
}
println(etapaVida) // Imprime "Adultez"

Las expresiones de rango con in permiten verificar si edad está dentro de ciertos límites.

Otra característica útil es que tanto if como when pueden utilizarse como expresiones lambda y retornarse o asignarse a variables para su posterior uso.

Finalmente, es recomendable utilizar when en lugar de múltiples if-else cuando se trabaja con múltiples condiciones, ya que mejora la legibilidad y mantenimiento del código.

Bucles (for, while, do-while)

Los bucles en Kotlin permiten ejecutar un bloque de código repetidamente hasta que se cumpla una condición específica. Son esenciales para iterar sobre colecciones o ejecutar tareas repetitivas.

El bucle for en Kotlin se utiliza para iterar sobre rangos, matrices y colecciones. Su sintaxis es simple y legible:

for (elemento in coleccion) {
    // Código a ejecutar
}

Por ejemplo, para recorrer una lista de números:

val numeros = listOf(1, 2, 3, 4, 5)
for (numero in numeros) {
    println(numero)
}

Este bucle imprimirá cada número de la lista en una nueva línea.

Además, es posible iterar sobre un rango de números utilizando el operador ..:

for (i in 1..5) {
    println("Contador: $i")
}

Este código mostrará los números del 1 al 5 inclusive.

Si se desea iterar de manera decreciente, se puede utilizar la función downTo:

for (i in 5 downTo 1) {
    println("Cuenta atrás: $i")
}

Para saltar elementos en el rango, se utiliza step:

for (i in 1..10 step 2) {
    println("Número impar: $i")
}

El bucle while ejecuta un bloque de código mientras una condición sea verdadera:

var contador = 0
while (contador < 5) {
    println("Contador: $contador")
    contador++
}

En este ejemplo, el bucle continuará hasta que el contador sea mayor o igual a 5.

El bucle do-while es similar a while, pero garantiza que el bloque de código se ejecute al menos una vez, ya que la condición se evalúa al final:

var numero = 0
do {
    println("Número: $numero")
    numero++
} while (numero < 3)

Aquí, incluso si la condición no se cumple desde el inicio, el código dentro del bloque do se ejecutará una vez.

Es importante manejar correctamente las condiciones de salida para evitar bucles infinitos, que pueden provocar que la aplicación deje de responder.

Para iterar sobre mapas, es posible acceder tanto a las claves como a los valores:

val mapa = mapOf("A" to 1, "B" to 2, "C" to 3)
for ((clave, valor) in mapa) {
    println("Clave: $clave, Valor: $valor")
}

De esta manera, se obtienen directamente los pares clave-valor sin necesidad de acceder a ellos por separado.

También es posible utilizar índices al recorrer colecciones con for:

val letras = listOf("a", "b", "c")
for (indice in letras.indices) {
    println("Índice: $indice, Letra: ${letras[indice]}")
}

En este caso, letras.indices proporciona el rango de índices válidos para la lista.

Para una iteración más segura y concisa, Kotlin ofrece funciones como forEach, especialmente útiles con colecciones. Sin embargo, el uso de bucles tradicionales sigue siendo válido y efectivo.

Al trabajar con secuencias o colecciones grandes, es recomendable considerar el uso de sequences para mejorar el rendimiento y evitar cargas innecesarias en memoria.

Expresiones de control (break, continue, return)

Las expresiones de control en Kotlin, como break, continue y return, permiten modificar el flujo de ejecución de un programa de manera precisa. Estas expresiones son esenciales para gestionar cómo y cuándo se ejecutan ciertas partes del código dentro de bucles y funciones.

La expresión break se utiliza para salir inmediatamente de un bucle. Cuando el programa encuentra un break, interrumpe la iteración actual y continúa con la ejecución del código que sigue al bucle. Por ejemplo:

for (i in 1..10) {
    if (i == 5) {
        break
    }
    println("Número: $i")
}
// Este código imprime los números del 1 al 4

En este caso, cuando i es igual a 5, el bucle se detiene gracias al break.

La expresión continue sirve para omitir la iteración actual y pasar a la siguiente dentro de un bucle. Es útil cuando se desea saltar ciertas condiciones sin abandonar completamente el bucle:

for (i in 1..5) {
    if (i == 3) {
        continue
    }
    println("Iteración: $i")
}
// Este código imprime los números 1, 2, 4 y 5

Aquí, cuando i es 3, el continue hace que se salte el println y se pase a la siguiente iteración.

La expresión return se utiliza para finalizar la ejecución de una función y, opcionalmente, devolver un valor. Es fundamental en la definición de funciones que necesitan devolver resultados:

fun obtenerMensaje(valor: Int): String {
    if (valor > 0) {
        return "El número es positivo"
    }
    return "El número no es positivo"
}
println(obtenerMensaje(5))
// Imprime "El número es positivo"

En este ejemplo, el return devuelve una cadena y finaliza la ejecución de la función obtenerMensaje.

En Kotlin, es posible utilizar etiquetas (labels) para controlar el flujo de bucles anidados de manera más específica. Una etiqueta se define con un identificador seguido de @ y se coloca antes de un bucle. Luego, se puede referenciar esa etiqueta con break o continue:

loopPrincipal@ for (i in 1..3) {
    for (j in 1..3) {
        if (i == 2 && j == 2) {
            break@loopPrincipal
        }
        println("i = $i, j = $j")
    }
}
// Este código imprime las combinaciones hasta que i = 2 y j = 2

Con break@loopPrincipal, se interrumpe el bucle etiquetado como loopPrincipal, no solo el bucle interno.

Las etiquetas también son útiles con return en funciones anónimas y expresiones lambda. En Kotlin, las lambdas pueden acceder al return de su función contenedora usando etiquetas:

fun buscarNúmero(números: List<Int>) {
    números.forEach label@{
        if (it == 0) {
            return@label
        }
        println("Número: $it")
    }
    println("Búsqueda completada")
}
buscarNúmero(listOf(1, 2, 0, 4))
// Imprime "Número: 1", "Número: 2" y luego "Búsqueda completada"

En este caso, return@label solo termina la ejecución de la lambda actual, no la de la función buscarNúmero.

Cuando se trabaja con funciones inlinadas, el comportamiento de return puede variar. En funciones inlinadas, un return dentro de una lambda puede devolver desde la función que la contiene. Sin embargo, es necesario entender cómo afecta esto al flujo del programa:

inline fun procesar(números: List<Int>, acción: (Int) -> Unit) {
    for (número in números) {
        acción(número)
    }
}

fun main() {
    procesar(listOf(1, 2, 3)) {
        if (it == 2) return
        println("Procesando: $it")
    }
    println("Programa finalizado")
}
// Imprime "Procesando: 1" y luego "Programa finalizado"

Aquí, el return dentro de la lambda termina la ejecución de main debido a que procesar es una función inlinada.

Es importante ser consciente del uso de break, continue y return para evitar comportamientos inesperados en el programa. Estas expresiones proporcionan un control detallado sobre la ejecución, lo que ayuda a construir algoritmos más eficientes y claros.

Expresiones de control con try-catch

En Kotlin, las expresiones de control con try-catch permiten manejar excepciones de manera más flexible y funcional. A diferencia de otros lenguajes, en Kotlin el bloque try-catch puede utilizarse como una expresión que devuelve un valor, lo que facilita el control del flujo del programa ante posibles errores.

El uso básico de try-catch consiste en envolver el código que podría lanzar una excepción dentro de un bloque try, y luego capturar dicha excepción en un bloque catch. Por ejemplo:

try {
    // Código que puede generar una excepción
} catch (e: ExceptionType) {
    // Manejo de la excepción
}

Sin embargo, al ser una expresión, es posible asignar el resultado del bloque try-catch a una variable. Esto es útil cuando se desea asignar un valor dependiendo de si se produce una excepción o no:

val resultado: Int = try {
    val divisor = 0
    10 / divisor
} catch (e: ArithmeticException) {
    println("Error: División por cero")
    0
}
println("Resultado: $resultado")

En este ejemplo, si se produce una excepción aritmética por división entre cero, el bloque catch devuelve 0, que es asignado a la variable resultado. De este modo, el programa continúa su ejecución sin interrumpirse.

Es importante destacar que el bloque catch en Kotlin puede devolver un valor, lo que permite que toda la expresión try-catch tenga un valor de retorno. Esto es especialmente útil en contextos donde se requiere un valor, como en inicializaciones o asignaciones.

Además, se puede utilizar un bloque finally que se ejecuta siempre, independientemente de si se lanzó una excepción o no. Este bloque es ideal para liberar recursos o realizar acciones que deben ejecutarse en cualquier caso:

val conexion = obtenerConexion()
val datos = try {
    conexion.leerDatos()
} catch (e: IOException) {
    println("Error al leer datos")
    emptyList()
} finally {
    conexion.cerrar()
}

En este caso, el método cerrar() se llama siempre, asegurando que la conexión se libere adecuadamente.

Las expresiones try-catch también pueden anidarse o combinarse con otras estructuras de control. Por ejemplo, es posible utilizar una expresión when dentro de un bloque catch para manejar diferentes tipos de excepciones:

val resultado = try {
    funcionRiesgosa()
} catch (e: Exception) {
    when (e) {
        is IOException -> {
            println("Error de E/S")
            -1
        }
        is NullPointerException -> {
            println("Referencia nula")
            -2
        }
        else -> {
            println("Error inesperado")
            -3
        }
    }
}

Aquí, según el tipo de excepción capturada, se realiza un manejo específico y se devuelve un valor acorde.

Otra característica es que las excepciones en Kotlin son unchecked, lo que significa que no es obligatorio declararlas o capturarlas. Sin embargo, es una buena práctica manejar las excepciones que puedan afectar la lógica de nuestro programa.

También es posible lanzar excepciones utilizando la palabra clave throw como expresión. Por ejemplo:

val numero = -5
val raizCuadrada = if (numero >= 0) {
    kotlin.math.sqrt(numero.toDouble())
} else {
    throw IllegalArgumentException("El número debe ser positivo")
}

En este caso, si numero es negativo, se lanza una excepción IllegalArgumentException, deteniendo la ejecución normal del programa. Esta excepción puede ser capturada más adelante con un bloque try-catch.

Cuando se trabaja con operaciones que pueden fallar, como conversión de cadenas a números, las expresiones try-catch son de gran utilidad:

val entrada = "123a"
val numero = try {
    entrada.toInt()
} catch (e: NumberFormatException) {
    println("Entrada inválida")
    0
}
println("Número: $numero")

Si la conversión falla debido a caracteres no numéricos, se captura la excepción y se asigna un valor por defecto.

Para mejorar la legibilidad y manejo de errores, Kotlin ofrece funciones como runCatching, que encapsulan la lógica de try-catch y proporcionan un objeto Result:

val resultado = runCatching {
    operacionPeligrosa()
}
if (resultado.isSuccess) {
    println("Operación exitosa: ${resultado.getOrNull()}")
} else {
    println("Error en la operación: ${resultado.exceptionOrNull()}")
}

Utilizando runCatching, se obtiene un resultado que puede ser evaluado para verificar si la operación tuvo éxito o si ocurrió una excepción.

En resumen, las expresiones de control con try-catch en Kotlin ofrecen una forma concisa y funcional de manejar excepciones y errores, integrándose de manera natural con el flujo del programa y permitiendo asignar valores en función de posibles fallos. Esto facilita la escritura de código más robusto y claro.

Aprende Kotlin GRATIS online

Ejercicios de esta lección Estructuras de control

Evalúa tus conocimientos de esta lección Estructuras de control con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Todas las lecciones de Kotlin

Accede a todas las lecciones de Kotlin y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Kotlin

Kotlin

Introducción Y Entorno

Instalación Y Primer Programa De Kotlin

Kotlin

Introducción Y Entorno

Tipos De Datos, Variables Y Constantes

Kotlin

Sintaxis

Operadores Y Expresiones

Kotlin

Sintaxis

Cadenas De Texto Y Manipulación

Kotlin

Sintaxis

Estructuras De Control

Kotlin

Sintaxis

Funciones Y Llamada De Funciones

Kotlin

Sintaxis

Clases Y Objetos

Kotlin

Programación Orientada A Objetos

Herencia Y Polimorfismo

Kotlin

Programación Orientada A Objetos

Interfaces Y Clases Abstractas

Kotlin

Programación Orientada A Objetos

Data Classes Y Destructuring

Kotlin

Programación Orientada A Objetos

Tipos Genéricos Y Varianza

Kotlin

Programación Orientada A Objetos

Listas, Conjuntos Y Mapas

Kotlin

Estructuras De Datos

Introducción A La Programación Funcional

Kotlin

Programación Funcional

Funciones De Primera Clase Y De Orden Superior

Kotlin

Programación Funcional

Inmutabilidad Y Datos Inmutables

Kotlin

Programación Funcional

Composición De Funciones

Kotlin

Programación Funcional

Monads Y Manejo Funcional De Errores

Kotlin

Programación Funcional

Operaciones Funcionales En Colecciones

Kotlin

Programación Funcional

Transformaciones En Monads Y Functors

Kotlin

Programación Funcional

Funciones Parciales Y Currificación

Kotlin

Programación Funcional

Introducción A Las Corutinas

Kotlin

Coroutines Y Asincronía

Asincronía Con Suspend, Async Y Await

Kotlin

Coroutines Y Asincronía

Concurrencia Funcional

Kotlin

Coroutines Y Asincronía

Evaluación

Kotlin

Evaluación

Accede GRATIS a Kotlin y certifícate

Certificados de superación de Kotlin

Supera todos los ejercicios de programación del curso de Kotlin y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

En esta lección

Objetivos de aprendizaje de esta lección

  1. Comprender y utilizar estructuras if, else, y when.
  2. Implementar bucles con for, while, y do-while.
  3. Utilizar expresiones de control break, continue, y return.
  4. Manejar excepciones con try-catch y comprender su uso como expresiones.
  5. Aplicar etiquetas para gestionar el flujo en bucles anidados.
  6. Integrar expresiones de control funcionales para mejorar la robustez del código.