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ícateEstructuras 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.
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.
Clases genéricas con varianza y restricciones
Introducción a las corutinas
Uso de asincronía con suspend, async y await
Formateo de cadenas texto
Uso de monads y manejo funcional de errores
Declaración y uso de variables y constantes
Uso de la concurrencia funcional con corutinas
Operaciones en colecciones
Uso de clases y objetos en Kotlin
Evaluación Kotlin
Funciones de orden superior y expresiones lambda en Kotlin
Herencia y polimorfismo en Kotlin
Inmutabilidad y datos inmutables
Uso de funciones parciales y currificaciones
Primer programa en Kotlin
Introducción a la programación funcional
Introducción a Kotlin
Uso de operadores y expresiones
Sistema de inventario de tienda
Uso de data classes y destructuring
Composición de funciones
Uso de interfaces y clases abstractas
Simulador de conversión de monedas
Programación funcional y concurrencia
Creación y uso de listas, conjuntos y mapas
Transformación en monads y functors
Crear e invocar funciones
Uso de las estructuras de control
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
Introducción Y Entorno
Instalación Y Primer Programa De Kotlin
Introducción Y Entorno
Tipos De Datos, Variables Y Constantes
Sintaxis
Operadores Y Expresiones
Sintaxis
Cadenas De Texto Y Manipulación
Sintaxis
Estructuras De Control
Sintaxis
Funciones Y Llamada De Funciones
Sintaxis
Clases Y Objetos
Programación Orientada A Objetos
Herencia Y Polimorfismo
Programación Orientada A Objetos
Interfaces Y Clases Abstractas
Programación Orientada A Objetos
Data Classes Y Destructuring
Programación Orientada A Objetos
Tipos Genéricos Y Varianza
Programación Orientada A Objetos
Listas, Conjuntos Y Mapas
Estructuras De Datos
Introducción A La Programación Funcional
Programación Funcional
Funciones De Primera Clase Y De Orden Superior
Programación Funcional
Inmutabilidad Y Datos Inmutables
Programación Funcional
Composición De Funciones
Programación Funcional
Monads Y Manejo Funcional De Errores
Programación Funcional
Operaciones Funcionales En Colecciones
Programación Funcional
Transformaciones En Monads Y Functors
Programación Funcional
Funciones Parciales Y Currificación
Programación Funcional
Introducción A Las Corutinas
Coroutines Y Asincronía
Asincronía Con Suspend, Async Y Await
Coroutines Y Asincronía
Concurrencia Funcional
Coroutines Y Asincronía
Evaluación
Evaluación
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
- Comprender y utilizar estructuras
if
,else
, ywhen
. - Implementar bucles con
for
,while
, ydo-while
. - Utilizar expresiones de control
break
,continue
, yreturn
. - Manejar excepciones con
try-catch
y comprender su uso como expresiones. - Aplicar etiquetas para gestionar el flujo en bucles anidados.
- Integrar expresiones de control funcionales para mejorar la robustez del código.