ProgramaciónDesarrollador Kotlin, Desarrollador Backend

¿Cómo funciona el mecanismo de rangos numéricos (Range y Progression) en Kotlin, cómo crear rangos personalizados y para qué tareas utilizarlos?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Rangos (Range) y progresiones (Progression) — son un mecanismo incorporado en Kotlin para representar secuencias de valores con un paso determinado. Se utilizan a menudo en la programación de bucles, condiciones, iteración sobre colecciones y validación de datos. Range se introdujo como una forma de hacer que la sintaxis de Kotlin sea más concisa y expresiva en comparación con Java.

Historia del tema

En Java, estas tareas se resolvían con bucles for y while utilizando índices — lo cual era prolijo y propenso a errores. En Kotlin, aparecieron operadores compactos para crear rangos (por ejemplo, 1..10) y métodos para definir pasos.

Problema

  • La necesidad de transmitir fácilmente un rango de valores (por ejemplo, todos los valores numéricos de 1 a 100).
  • Mejora de la conveniencia al iterar y validar.
  • La posibilidad de sobrescribir rangos para tipos personalizados.

Solución

Kotlin proporciona rangos numéricos estándar (IntRange, LongRange, CharRange, UIntRange, etc.) y interfaces para crear progresiones personalizadas:

Ejemplo de código:

for (i in 1..5) print("$i ") // 1 2 3 4 5 for (i in 5 downTo 1 step 2) print("$i ") // 5 3 1 // Comprobación de valores val x = 42 if (x in 1..100) println("¡En el rango!")

Rangos personalizados

Puedes definir un rango para tus tipos implementando los operadores rangeTo y Progression:

data class Version(val major: Int, val minor: Int) : Comparable<Version> { override fun compareTo(other: Version) = compareValuesBy(this, other, Version::major, Version::minor) } operator fun Version.rangeTo(other: Version) = VersionRange(this, other) class VersionRange( override val start: Version, override val endInclusive: Version ) : ClosedRange<Version> for (v in Version(1, 0)..Version(1, 2)) println(v)

Características clave:

  • Sintaxis compacta para crear rangos (start..end, downTo, step).
  • Comprobaciones de pertenencia incorporadas (in, !in).
  • La posibilidad de definir rangos y progresiones para tipos personalizados.

Preguntas capciosas.

¿Qué devuelve la expresión 1..5 realmente?

Crea una instancia de la clase IntRange, que implementa la interfaz ClosedRange<Int>. No es una colección, sino un objeto que define límites y pasos. Implementación perezosa.

¿Por qué el paso (step) de Range siempre es 1? ¿Cómo se puede cambiar el paso?

Por defecto, el paso de un rango es 1 (o -1 para downTo). Para un paso diferente se utilizan los métodos step y downTo. Por ejemplo:

for (i in 2..10 step 2) println(i)

¿Se pueden usar rangos con tipos que no implementan Comparable?

No, para que funcionen correctamente los rangos personalizados, el tipo debe implementar la interfaz Comparable, de lo contrario, el operador rangeTo será imposible.

Errores típicos y anti-patrones

  • Intentar cambiar el paso de un Range sin usar el método step.
  • Utilizar un rango con tipos que no tienen Comparable.
  • Dirección confundida (por ejemplo, 5..1 no devolverá nada).

Ejemplo de la vida real

Caso negativo

El desarrollador utiliza el bucle for (i in 5..1) sin downTo, esperando que impima "5, 4, 3, 2, 1", pero al final el bucle no se ejecuta ni una vez.

Pros:

  • Simplicidad de la sintaxis.

Contras:

  • Comportamiento no obvio de rangos negativos.
  • Es fácil confundirse para un principiante.

Caso positivo

Uso de progresiones con downTo y step para iterar sobre informes con el intervalo requerido, haciendo que el código sea compacto y auto-documentado.

Pros:

  • Estilo conciso.
  • Pocas probabilidades de errores de desbordamiento.

Contras:

  • Necesidad de conocer las especificidades de los pasos y direcciones.