Introducción
Las funciones en Kotlin son más poderosas que en Java, con soporte para parámetros por defecto, argumentos con nombre y funciones de extensión que te permiten agregar funcionalidad a clases existentes.
Funciones Básicas
// Función simple
fun saludar(nombre: String) {
println("¡Hola, $nombre!")
}
// Función con tipo de retorno
fun sumar(a: Int, b: Int): Int {
return a + b
}
// Función de expresión única
fun multiplicar(a: Int, b: Int) = a * b
// Tipo de retorno Unit (como void en Java)
fun imprimirSuma(a: Int, b: Int): Unit {
println("Suma: ${a + b}")
}
Parámetros por Defecto
fun crearUsuario(
nombre: String,
edad: Int = 18,
pais: String = "Desconocido"
) {
println("Usuario: $nombre, $edad, $pais")
}
// Uso
crearUsuario("Alicia") // Usa valores por defecto
crearUsuario("Bob", 25) // Sobrescribe edad
crearUsuario("Carlos", 30, "USA") // Sobrescribe todos
Argumentos con Nombre
fun enviarEmail(
para: String,
asunto: String,
cuerpo: String,
cc: String = "",
cco: String = ""
) {
println("Para: $para, Asunto: $asunto")
}
// Llamada con argumentos con nombre
enviarEmail(
para = "[email protected]",
asunto = "Reunión",
cuerpo = "Reunámonos mañana",
cc = "[email protected]"
)
Funciones de Extensión
Agrega métodos a clases existentes sin herencia:
// Extender clase String
fun String.esPalindromo(): Boolean {
return this == this.reversed()
}
// Uso
println("anilina".esPalindromo()) // true
println("hola".esPalindromo()) // false
// Extender Int
fun Int.esPar() = this % 2 == 0
fun Int.esImpar() = this % 2 != 0
println(4.esPar()) // true
println(7.esImpar()) // true
Funciones Infijas
Crea código más legible similar a DSL:
infix fun Int.veces(str: String) = str.repeat(this)
// Uso
val resultado = 3 veces "Hola " // "Hola Hola Hola "
// Funciones infijas incorporadas
val par = "clave" to "valor" // to es una función infija
val rango = 1 until 10 // until es una función infija
Funciones de Orden Superior
Funciones que toman funciones como parámetros o retornan funciones:
// Función como parámetro
fun operarConNumeros(a: Int, b: Int, operacion: (Int, Int) -> Int): Int {
return operacion(a, b)
}
// Uso
val suma = operarConNumeros(5, 3) { x, y -> x + y }
val producto = operarConNumeros(5, 3) { x, y -> x * y }
// Función que retorna función
fun crearMultiplicador(factor: Int): (Int) -> Int {
return { numero -> numero * factor }
}
val duplicador = crearMultiplicador(2)
println(duplicador(5)) // 10
Expresiones Lambda
// Sintaxis lambda
val cuadrado: (Int) -> Int = { x -> x * x }
val suma: (Int, Int) -> Int = { a, b -> a + b }
// Parámetro único puede usar 'it'
val doble: (Int) -> Int = { it * 2 }
// Lambda multi-línea
val calcular = { a: Int, b: Int ->
val resultado = a + b
resultado * 2 // Última expresión se retorna
}
Funciones de Ámbito
let - Ejecutar código en no nulo:
val nombre: String? = "Alicia"
nombre?.let {
println("Longitud: ${it.length}")
println("Mayúsculas: ${it.uppercase()}")
}
apply - Configurar objetos:
val persona = Persona().apply {
nombre = "Alicia"
edad = 25
pais = "USA"
}
also - Realizar efectos secundarios:
val numeros = mutableListOf(1, 2, 3)
.also { println("Original: $it") }
.add(4)
run - Ejecutar bloque de código:
val resultado = run {
val a = 5
val b = 10
a + b // Retorna 15
}
with - Llamar múltiples métodos:
val mensaje = with(StringBuilder()) {
append("Hola")
append(" ")
append("Mundo")
toString()
}
Ejemplos Prácticos
Procesamiento de listas:
val numeros = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// Filtrar y mapear
val cuadradosPares = numeros
.filter { it.esPar() }
.map { it * it }
// [4, 16, 36, 64, 100]
// Operaciones personalizadas
fun List<Int>.sumaDeCuadrados() = this.map { it * it }.sum()
println(numeros.sumaDeCuadrados()) // 385
Extensiones de String:
fun String.truncar(longitudMaxima: Int): String {
return if (this.length <= longitudMaxima) this
else "${this.substring(0, longitudMaxima)}..."
}
println("Hola Mundo".truncar(5)) // "Hola..."
fun String.aTituloCapitalizado() = this.split(" ")
.joinToString(" ") { it.capitalize() }
println("hola mundo".aTituloCapitalizado()) // "Hola Mundo"
Validación con extensión:
fun String.esEmailValido() =
this.contains("@") && this.contains(".")
fun String.esNombreUsuarioValido() =
this.length >= 3 && this.all { it.isLetterOrDigit() || it == '_' }
val email = "[email protected]"
if (email.esEmailValido()) {
println("Email válido")
}
Buenas Prácticas
- Usa funciones de extensión para agregar métodos de utilidad
- Prefiere funciones de expresión única cuando sea posible
- Usa argumentos con nombre para mejor legibilidad
- Usa parámetros por defecto para reducir sobrecarga
- Mantén las expresiones lambda cortas y legibles
- Usa funciones de ámbito apropiadamente
Conclusión
Las características de funciones de Kotlin hacen el código más conciso y expresivo. Las funciones de extensión son particularmente poderosas para agregar funcionalidad sin modificar clases existentes.
En el próximo capítulo, exploraremos la seguridad de nulos en Kotlin.