Declaración de funciones
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
}
-
fun
fun
-
functionName
functionName
- Parámetros (
param1
param1
param2
param2
- Una función puede no tener parámetros.
- Es posible asignar valores por defecto, lo que permite omitir argumentos al llamarla.
-
-
ReturnType
ReturnType
- Si no devuelve un valor significativo, se utiliza
Unit
Unit
-
- 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 + b
Kotlin incluso permite inferir el tipo de retorno automáticamente:
fun add(a: Int, b: Int) = a + b
Este 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 (
).
= ...
= ...
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.
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.
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:
-
calculateTotal
calculateTotal
-
printMessage
printMessage
-
main
main
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.
Evita estilos heredados de otros lenguajes o que no son válidos en Kotlin:
-
CalculateTotal
CalculateTotal
-
calculate_total
calculate_total
-
calculate-total
calculate-total
-
CALCULATE_TOTAL
CALCULATE_TOTAL
vararg
vararg
)
Funciones variádicas (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
// ...
-
Array<out T>
Array<out T>
T
T
- Las versiones para
List
List
Set
Set
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()
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
vararg
vararg
*
*
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 (
DoubleArray
DoubleArray
FloatArray
FloatArray
- Arreglos genéricos (
Array<T>
Array<T>
- Colecciones (
List<T>
List<T>
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 10
Este 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
vararg
vararg
azúcar sintáctico[*]sobre una función que acepta un arreglo. - Puedes seguir llamando a la función con un arreglo existente usando el operador
*
*
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>
-
Array<T>
Array<T>
-
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 | | |
- Usa
List<T>
List<T>
- Usa
Array<T>
Array<T>
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 }
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 Conclusiones
Array<T>
Array<T>
y List<T>
List<T>
.
Puntos clave
- Las funciones se declaran con la palabra clave
fun
fun
- El tipo de retorno puede inferirse automáticamente si la función se reduce a una sola expresión.
-
vararg
vararg
- Es posible requerir al menos un argumento junto a
vararg
vararg
-
Array<T>
Array<T>
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.