Création d'un itérateur de plage Kotlin sur un objet personnalisé

1. Introduction

Dans un article précédent, nous avons montré comment créer une plage dans Kotlin et à quel point il est facile d'itérer sur les types Int, Long et Char .

Mais que faire si nous voulons itérer sur un type personnalisé ? C'est possible? La réponse est oui! Alors passons au code et voyons comment.

2. Un type coloré

Imaginons que nous ayons une classe simple qui représente une couleur RVB:

class CustomColor(val rgb: Int): Comparable {} 

Ce serait bien de pouvoir itérer sur une gamme de couleurs RVB:

val a = CustomColor(0x000000) val b = CustomColor(0xCCCCCC) for (cc in a..b) { // do things }

3. Un coup d' œil sur IntRange

En termes simples, nous devrons implémenter Comparable , Iterable et ClosedRange. De notre article précédent, nous savons déjà que nous devrons implémenter Comparable.

Pour les deux autres interfaces, plongeons dans la déclaration de classe IntRange pour quelques astuces:

public class IntRange(start: Int, endInclusive: Int) : IntProgression(start, endInclusive, 1), ClosedRange 

Et puis, la déclaration d' IntProgression montre qu'elle implémente Iterable:

public open class IntProgression : Iterable

Donc, nous allons vouloir faire quelque chose de similaire pour que cela fonctionne.

4. Classe ColorRange

Comme IntRange , créons une classe ColorRange .

Pour nos besoins, nous ignorerons également l' imitation d' IntProgression, car nous sommes d'accord pour avoir une étape par défaut de 1. Cela simplifiera un peu les choses et nous permettra simplement d' implémenter directement ClosedRange et Iterable :

class ColorRange(override val start: CustomColor, override val endInclusive: CustomColor) : ClosedRange, Iterable{ override fun iterator(): Iterator { return ColorIterator(start, endInclusive) } }

Pour notre implémentation de iterator () , nous retournerons une classe ColorIterator qui fera le gros du travail pour parcourir la plage.

Parce que ColorRange implémente le ClosedRange interface, nous devons implémenter la méthode compareTo sur la classe CustomColor :

override fun compareTo(other: CustomColor): Int { return this.rgb.compareTo(other.rgb) }

5. Classe ColorIterator

ColorIterator est la dernière pièce du puzzle:

class ColorIterator(val start: CustomColor, val endInclusive: CustomColor) : Iterator { var initValue = start override fun hasNext(): Boolean { return initValue <= endInclusive } override fun next(): CustomColor { return initValue++ } }

Notez que initValue est de type CustomColor. Donc, pour le muter avec l' opérateur ++ , nous devons également ajouter la méthode inc () à CustomColor :

operator fun inc(): CustomColor { return CustomColor(rgb + 1) }

6. Utilisation de la plage personnalisée

Nous y sommes presque!

Puisque nous définissons notre plage personnalisée, la classe CustomColor doit implémenter la méthode rangeTo . La méthode rangeTo nous permettra d'itérer sur notre plage en utilisant l' opérateur .. , un peu comme l'ajout de inc nous permet d'utiliser l' opérateur ++ .

Voyons le produit final:

class CustomColor(val rgb: Int): Comparable { override fun compareTo(other: CustomColor): Int { return this.rgb.compareTo(other.rgb) } operator fun rangeTo(that: CustomColor) = ColorRange(this,that) operator fun inc(): CustomColor { return CustomColor(rgb + 1) } }

Et c'est tout ce dont nous avons besoin!

Enfin, voyons comment tout cela fonctionne ensemble, en utilisant une plage de notre classe CustomColor :

@Test fun assertHas10Colors(){ assertTrue { val a = CustomColor(1) val b = CustomColor(10) val range = a..b for (cc in range) { println(cc) } range.toList().size == 10 } }

Dans ce test, nous avons défini une variable de plage et utilisé pour parcourir les objets CustomColor , ainsi que pour les transformer en une liste.

Voyons un autre exemple d'utilisation de la méthode contient standard sur la plage:

@Test fun assertContains0xCCCCCC(){ assertTrue { val a = CustomColor(0xBBBBBB) val b = CustomColor(0xDDDDDD) val range = a..b range.contains(CustomColor(0xCCCCCC)) } }

7. Conclusion

Kotlin a une implémentation native de range pour les valeurs Int, Long et Char . Dans cet article, nous avons appris à implémenter une plage sur une classe personnalisée.

Comme toujours, le code est disponible sur GitHub.