ProgrammationDéveloppeur Kotlin, Développeur Backend

Comment fonctionne le mécanisme des plages numériques (Range et Progression) en Kotlin, comment créer ses propres plages et pour quelles tâches les utiliser ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Plages (Range) et progressions (Progression) — c'est un mécanisme intégré dans Kotlin pour représenter des séquences de valeurs avec un pas défini. Ils sont souvent utilisés lors de la manipulation de boucles, de conditions, de l'itération sur des collections et de la validation des données. La plage est apparue comme l'un des moyens de rendre la syntaxe de Kotlin plus concise et expressive par rapport à Java.

Historique de la question

En Java, des tâches similaires étaient résolues via des boucles for et while avec des indices — c'était verbeux et sujet à erreur. En Kotlin, des opérateurs compacts pour la création de plages sont apparus (par exemple, 1..10) ainsi que des méthodes pour définir des pas.

Problématique

  • Nécessité de transmettre facilement une plage de valeurs (par exemple, toutes les valeurs numériques de 1 à 100).
  • Augmentation de la commodité lors de l'itération et de la validation.
  • Possibilité de redéfinir les plages pour des types personnalisés.

Solution

Kotlin fournit des plages numériques standard (IntRange, LongRange, CharRange, UIntRange, etc.) et des interfaces pour créer ses propres progressions :

Exemple de code :

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 // Vérification de valeur val x = 42 if (x in 1..100) println("Dans la plage !")

Plages personnalisées

On peut définir une plage pour ses types en implémentant les opérateurs rangeTo et 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)

Caractéristiques clés :

  • Syntaxe compacte pour la création de plages (start..end, downTo, step).
  • Vérifications intégrées d'appartenance (in, !in).
  • Possibilité de définir des plages et progressions pour des types personnalisés.

Questions pièges.

Que renvoie l'expression 1..5 en réalité ?

Elle crée une instance de la classe IntRange, qui implémente l'interface ClosedRange<Int>. Ce n'est pas une collection, mais un objet définissant les limites et le pas. Mise en œuvre paresseuse.

Pourquoi le pas (step) de Range est toujours 1 ? Comment modifier le pas ?

Par défaut, le pas d'une plage est de 1 (ou -1 lors de downTo). Pour un pas différent, on utilise les méthodes step et downTo. Par exemple :

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

Peut-on utiliser des plages avec des types ne réalisant pas Comparable ?

Non, pour le bon fonctionnement d'une plage personnalisée, le type doit implémenter l'interface Comparable, sinon l'opérateur rangeTo ne sera pas possible.

Erreurs typiques et anti-modèles

  • Essayer de modifier le pas d'une Range sans utiliser la méthode step.
  • Utilisation d'une plage avec des types sans Comparable.
  • Direction mélangée (par exemple, 5..1 ne donnera rien).

Exemple de la vie réelle

Cas négatif

Un développeur utilise une boucle for (i in 5..1) sans downTo, s'attendant à "5, 4, 3, 2, 1", mais au final, la boucle ne s'exécute jamais.

Avantages :

  • Simplicité de la syntaxe.

Inconvénients :

  • Comportement non évident d'une plage négative.
  • Facile de se perdre pour un débutant.

Cas positif

Utilisation de la progression avec downTo et step pour itérer sur des rapports avec l'intervalle souhaité, rendant le code compact et auto-documenté.

Avantages :

  • Style concis.
  • Peu de chances d'erreurs d'évasion.

Inconvénients :

  • Il faut connaître les spécificités des pas et des directions.