Declaración de funciones
Abstract
Las funciones son una herramienta esencial para construir software reutilizable, legible y organizado. Kotlin facilita su declaración mediante una sintaxis concisa y expresiva, que incorpora características como parámetros con valores por defecto, funciones de una sola expresión e inferencia de tipos.
En esta lección aprenderás a declarar funciones en Kotlin, comprender cómo
utilizar para aceptar múltiples
argumentos y cuándo conviene especificar tipos de retorno. También explorarás
funciones estándar como vararg vararg y
map map , y conocerás las diferencias
entre colecciones como fold fold y
Array Array .
List List
No es necesario dominar todos los detalles desde el principio: a medida que avancemos en el curso, retomaremos y profundizaremos los conceptos más relevantes.
Funciones en Kotlin
Una función en Kotlin es un bloque de código reutilizable que encapsula una tarea específica. Su sintaxis básica es la siguiente:
fun functionName(param1: Type1, param2: Type2 = defaultValue, ...): ReturnType {
// Cuerpo de la función
return result
} Explicación de la sintaxis
-
: Palabra clave para declarar una función.funfun -
: Nombre que identifica a la función; debe ser claro y representativo de su propósito.functionNamefunctionName - Parámetros (
,param1param1): Lista de argumentos con sus respectivos tipos.param2param2- Una función puede no tener parámetros.
- Es posible asignar valores por defecto, lo que permite omitir argumentos al llamarla.
-
-
: Tipo del valor que retorna la función.ReturnTypeReturnType- Si no devuelve un valor significativo, se utiliza
, aunque puede omitirse por ser implícito.UnitUnit
-
- Cuerpo de la función: Conjunto de instrucciones que se ejecutan cuando se llama.
Ejemplo: Sumar dos números
Supongamos que queremos crear una función que sume dos números enteros. La declaración básica sería:
fun add(a: Int, b: Int): Int {
return a + b
}Si la función contiene solo una expresión, puedes escribirla de forma más concisa utilizando asignación directa:
fun add(a: Int, b: Int): Int = a + bKotlin incluso permite inferir el tipo de retorno automáticamente:
fun add(a: Int, b: Int) = a + bEste estilo hace que el código sea más breve, pero conviene usarlo con moderación: puede dificultar la lectura si el tipo de retorno no es evidente, y volver el código más frágil frente a cambios accidentales.
Si vienes de Scala...
En Kotlin, la palabra clave es obligatoria
cuando utilizas el bloque clásico return return .
Esto contrasta con Scala, donde la última expresión de una función se retorna automáticamente
sin necesidad de usar { ... } { ... } .
return return
En Kotlin, ese comportamiento implícito solo se aplica cuando se utiliza la sintaxis de una sola expresión ( ).
= ... = ...
Inferencia de tipos
Kotlin es un lenguaje con inferencia de tipos, lo que significa que el compilador puede deducir el tipo de una variable o expresión a partir del contexto. En el ejemplo anterior, dado que la función contiene una sola expresión, el tipo de retorno se infiere automáticamente.
No abuses de la inferencia
Aunque la inferencia puede hacer el código más limpio y conciso, no siempre es recomendable omitir los tipos. En funciones públicas, con lógica compleja o que formen parte de una API, declarar el tipo explícitamente mejora la legibilidad, actúa como documentación autoexplicativa y facilita la evolución del código a largo plazo.
Estilo de nombres
En Kotlin, las funciones y variables deben nombrarse usando la convención camelCase. Esto implica lo siguiente:
- El nombre comienza con una letra minúscula.
- Cada palabra siguiente se escribe sin espacios, iniciando con mayúscula.
Ejemplos correctos:
-
calculateTotalcalculateTotal -
printMessageprintMessage -
mainmain
Usar un estilo de nombres consistente mejora la legibilidad y asegura que tu código esté alineado con las prácticas idiomáticas de Kotlin.
Estilos incorrectos
Evita estilos heredados de otros lenguajes o que no son válidos en Kotlin:
-
— PascalCase, reservado para clases y tipos.CalculateTotalCalculateTotal -
— snake_case, típico en Python, no se utiliza en Kotlin.calculate_totalcalculate_total -
— kebab-case, inválido como identificador.calculate-totalcalculate-total -
— UPPER_SNAKE_CASE, reservado para constantes y valores inmutables en tiempo de compilación.CALCULATE_TOTALCALCULATE_TOTAL
Trailing lambdas (λ al final)
En Kotlin, si el último parámetro de una función es otra función (un function type), puedes pasar la lambda fuera de los paréntesis. Si además es el único parámetro, puedes omitir los paréntesis. Esto mejora la legibilidad, especialmente con operaciones sobre colecciones y DSLs.
// Sin trailing lambda
list.filter({ x -> x > 10 }).map({ x -> x * 2 })
// Con trailing lambda: paréntesis opcionales
list.filter() { x -> x > 10 }.map() { x -> x * 2 }
// Con trailing lambda: la lambda sale de los paréntesis
list.filter { x -> x > 10 }.map { x -> x * 2 } ¿Qué acabamos de hacer?
- Último parámetro funcional → la lambda puede ir fuera de
.(...)(...) - Único parámetro → puedes omitir por completo
.(...)(...) - Útil en
,mapmap,filterfilter, DSLs (foldfold,applyapply,withwith), etc.buildStringbuildString
Lectura fluida
list.map { ... } list.map { ... } introduce menos
"ruido visual" que list.map({ ... }) list.map({ ... }) .
Ambigüedades y sobrecargas
(...) (...) .
Retornos desde lambdas
Dentro de una lambda, intenta salir de la
función envolvente (no de la lambda) en lambdas en línea. Para
volver desde la propia lambda, usa return return :
return@nombre return@nombre
users.forEach {
if (it.age < 0) return@forEach // solo salta esta iteración
println(it.name)
}
Funciones variádicas ( vararg vararg )
vararg vararg
En Kotlin puedes definir funciones que aceptan una cantidad variable de argumentos usando la palabra clave . Esto permite
invocar la función con cero, uno o más valores del mismo
tipo, de manera similar a vararg vararg en Python
o *args *args en JavaScript.
...args ...args
fun sumAll(vararg nums: Int): Int =
nums.sum() sum() sum()
sum() sum()
La función
es una extensión que suma todos los elementos de una colección o arreglo.
sum() sum()
En el caso de , su firma es:
IntArray IntArray
fun IntArray.sum(): Int
Esto equivale a realizar:
.
nums[0] + nums[1] + ... + nums[n-1] nums[0] + nums[1] + ... + nums[n-1]
También existen variantes para otros tipos de colecciones numéricas:
fun Array<out Int>.sum(): Int
fun Iterable<Double>.sum(): Double
// ...-
es una colección covariante, lo que significa que puedes pasar cualquier subtipo deArray<out T>Array<out T>sin modificar la colección.TTEl principio get-put establece que un tipo covariante puede utilizarse como tipo de retorno (
) pero no como tipo de entrada (outout).ininEn este caso,
indica que el arreglo solo se emplea para obtener valores, no para insertarlos.outout - Las versiones para
,ListListy otrosSetSetfuncionan del mismo modo.Iterable<T>Iterable<T>
sumAll(1, 2, 3, 4) // devuelve 10
sumAll() // devuelve 0
Si ya tienes un arreglo existente, puedes desempaquetar sus elementos
usando el prefijo al pasarlo como argumento:
* *
val extras = intArrayOf(5, 6)
sumAll(1, 2, *extras) // devuelve 14 * * solo funciona con arreglos
* *
El operador para desempaquetar argumentos funciona
exclusivamente con arreglos (* * o tipos primitivos
como Array Array ). Si tienes una
IntArray IntArray , primero debes convertirla a un arreglo
usando
List List :
list.toTypedArray() list.toTypedArray()
val extrasList = listOf(5, 6)
sumAll(1, 2, *extrasList.toTypedArray()) // devuelve 14
Ten en cuenta que crea una copia
completa de la lista en memoria. En colecciones grandes, esto implica una
asignación y recorrido adicionales, lo que puede afectar el rendimiento
y el consumo de memoria.
toTypedArray() toTypedArray()
Hack: Uno o más argumentos
Si necesitas que una función reciba al menos un argumento obligatorio, declara ese argumento como un parámetro normal y colócalo antes de
un
.
vararg vararg
Una forma sencilla de recordarlo es compararlo con las expresiones regulares:
- representa “cero o más repeticiones”
- representa “una o más repeticiones”
En este contexto:
-
El parámetro obligatorio equivale a
.++ -
El
equivale avarargvararg.**
Combinados, expresan la idea de “uno o más argumentos”.
fun sumTo(first: Int, vararg rest: Int): Int =
rest.fold(first) { acc, i -> acc + i } fold() fold()
fold() fold()
La función
es una extensión que aplica una operación acumulativa sobre los elementos
de una colección, comenzando desde un valor inicial.
fold() fold()
En este caso, para :IntArray IntArray
fun IntArray.fold(initial: Int, operation: (acc: Int, Int) -> Int): Int
Esto equivale a:
.
initial + this[0] + this[1] + ... initial + this[0] + this[1] + ...
también se puede usar con:
fold() fold()
- Arreglos primitivos (
,DoubleArrayDoubleArray, etc.)FloatArrayFloatArray - Arreglos genéricos (
)Array<T>Array<T> - Colecciones (
,List<T>List<T>, etc.)Set<T>Set<T>
Versiones generalizadas:
fun <T, R> Array<out T>.fold(initial: R, operation: (acc: R, T) -> R): R
fun <T, R> Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R
Aquí es el tipo del acumulador,
R R el tipo de los elementos, y
T T indica covarianza: puedes pasar subtipos
de
out out como elementos.
T T
sumTo(1) // devuelve 1
sumTo(1, 2, 3, 4) // devuelve 10
sumTo(first = 1, rest = intArrayOf(2, 3, 4)) // devuelve 10Este patrón también sirve para funciones con dos o más parámetros obligatorios, seguidos por una cantidad variable de argumentos opcionales.
vararg vararg vs. Array Array
vararg vararg Array Array
En Kotlin, es una forma conveniente de permitir
que una función reciba una cantidad variable de argumentos. Internamente, el compilador convierte esos argumentos en un arreglo
(vararg vararg , Array<T> Array<T> ,
etc.).
IntArray IntArray
Esto significa que:
- Usar
es azúcar sintáctico[*] Se llama azúcar sintáctica (syntactic sugar) a una forma más conveniente o legible de escribir algo que, en realidad, se traduce a una forma más básica del lenguaje. sobre una función que acepta un arreglo.varargvararg - Puedes seguir llamando a la función con un arreglo existente usando el operador
para desempaquetarlo.**
vararg vararg vs. pasando un arreglo fun sumAll(vararg nums: Int): Int = nums.sum()
val valores = intArrayOf(1, 2, 3)
sumAll(*valores) // ✅ correcto: desempaquetado con *
Kotlin optimiza los tipos primitivos ( , Int Int , etc.) usando Double Double , IntArray IntArray , etc., en lugar de DoubleArray DoubleArray o Array<Int> Array<Int> . Esto mejora el rendimiento y evita el
boxing
innecesario.
Array<Double> Array<Double>
Array<T> Array<T> vs. List<T> List<T>
Array<T> Array<T> vs. List<T> List<T>
Tanto como Array<T> Array<T>
representan colecciones de tamaño fijo e
inmutables en cuanto a estructura (no puedes agregar
ni quitar elementos). Sin embargo, tienen propósitos diferentes: un
List<T> List<T> es mutable en sus elementos y está más
orientado a la eficiencia y la interoperabilidad con Java, mientras que
Array<T> Array<T> es más expresiva y está pensada para un
estilo idiomático y funcional.
List<T> List<T>
-
es una estructura de bajo nivel, más cercana al funcionamiento de los arreglos en lenguajes como Java.Array<T>Array<T> -
es parte de la API de colecciones de Kotlin, más expresiva y flexible para la programación funcional.List<T>List<T>
| Característica | | |
| Mutable |
Sí ( )
| No (estructura inmutable, pero sus elementos pueden ser mutables) |
| Tamaño fijo | Sí |
Sí ( , no en )
|
| Posiciones accesibles |
Por índice ( )
|
Por índice ( )
|
| Métodos funcionales | Limitados | Amplia API funcional |
| Uso común | Eficiencia, interoperabilidad con Java | Estilo idiomático, programación funcional |
| Conversión | | |
En resumen
- Usa
para la mayoría de los casos, especialmente si buscas inmutabilidad lógica y una API más rica y expresiva para manipulación de datos.List<T>List<T> - Usa
cuando necesites interoperar con código Java o cuando el rendimiento en acceso/modificación por índice sea crítico.Array<T>Array<T>
Ejercicio: Duplicar niveles de poder
Vamos a practicar la declaración de funciones en Kotlin aplicando una
transformación sobre una lista. Supón que tienes una lista de niveles de
poder ( ) y quieres duplicar cada uno de ellos
usando Int Int .
map() map()
Como recordatorio, aplica una función a cada
elemento de una colección y devuelve una nueva colección con los resultados:
map() map()
map() map() fun <T, R> List<T>.map(transform: (T) -> R): List<R>
fun <T, R> Array<out T>.map(transform: (T) -> R): List<R>
Tu tarea es definir una función llamada que reciba una lista de enteros (doublePowers doublePowers ) o
bien una cantidad variable de argumentos (List<Int> List<Int> )
y devuelva una nueva lista con cada valor duplicado.
vararg vararg
Ejemplos de uso
doublePowers(listOf(5, 10, 15))
// devuelve: [10, 20, 30] vararg vararg doublePowers(5, 10, 15)
// devuelve: [10, 20, 30]
doublePowers(*intArrayOf(5, 10, 15))
// devuelve: [10, 20, 30]Ambas versiones son equivalentes en funcionalidad, pero difieren en cómo se pasan los argumentos al invocar la función.
Solución
fun doublePowers(powers: List<Int>): List<Int> =
powers.map { it * 2 } vararg vararg fun doublePowers(vararg powers: Int): List<Int> =
powers.map { it * 2 }Conclusiones
En esta lección aprendimos la sintaxis básica para declarar funciones en Kotlin, un componente esencial para escribir código reutilizable, expresivo y mantenible. También exploramos variantes idiomáticas como la inferencia de tipos, las funciones variádicas y las diferencias clave entre colecciones como Array<T> Array<T> y List<T> List<T> .
Puntos clave
- Las funciones se declaran con la palabra clave
y pueden tener parámetros con valores por defecto.funfun - El tipo de retorno puede inferirse automáticamente si la función se reduce a una sola expresión.
-
permite definir funciones que aceptan una cantidad variable de argumentos.varargvararg - Es posible requerir al menos un argumento junto a
, combinándolos en la firma.varargvararg -
yArray<T>Array<T>parecen similares, pero tienen diferencias clave en mutabilidad y uso.List<T>List<T> - Kotlin favorece un estilo conciso, pero también explícito y claro cuando la función es pública o compleja.
¿Qué nos llevamos?
Esta lección fue tu primer paso en el sistema de funciones de Kotlin.
No es necesario memorizar cada detalle ahora: usa este material como referencia rápida cuando lo necesites.
Retomaremos estos conceptos más adelante, profundizando según lo requiera cada unidad.
En otros lenguajes…
¿Con ganas de más?
Referencias recomendadas
- “ Functions ” en Kotlin docs Documentación oficialEsta documentación ofrece una guía exhaustiva sobre la declaración y uso de funciones en Kotlin. Aborda desde conceptos básicos como la sintaxis con
, el uso de parámetros con tipo explícito y valores por defecto, hasta temas más avanzados como funciones variádicas (funfun), argumentos nombrados, funciones de una sola expresión, inferencia de tipos, funciones infijas (varargvararg), genéricas y recursivas de cola (infixinfix). También detalla cómo declarar funciones a nivel superior, funciones locales, de miembro y de extensión, promoviendo un estilo conciso, idiomático y expresivo. Es una fuente esencial para comprender el modelo funcional y flexible de funciones en Kotlin.tailrectailrec