Creazione di un iteratore di intervallo Kotlin su un oggetto personalizzato

1. Introduzione

In un articolo precedente, abbiamo mostrato come creare un intervallo in Kotlin e quanto sia facile iterare sui tipi Int, Long e Char .

Ma cosa succede se vogliamo iterare su un tipo personalizzato ? È possibile? La risposta è si! Quindi passiamo al codice e vediamo come.

2. Un tipo colorato

Immaginiamo di avere una semplice classe che rappresenta un colore RGB:

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

Sarebbe bello poter iterare su una gamma di colori RGB:

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

3. Una rapida occhiata a IntRange

In poche parole, dovremo implementare Comparable , Iterable e ClosedRange. Dal nostro articolo precedente, sappiamo già che dovremo implementare Comparable.

Per le altre due interfacce, tuffiamoci nella dichiarazione della classe IntRange per alcuni suggerimenti:

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

E poi, la dichiarazione di IntProgression mostra che implementa Iterable:

public open class IntProgression : Iterable

Quindi, vorremo fare qualcosa di simile per farlo funzionare.

4. ColorRange Class

Come IntRange , creiamo una classe ColorRange .

Per i nostri scopi, tralasciamo anche l' imitazione di IntProgression, dal momento che ci va bene avere un passaggio predefinito di 1. Questo semplificherà un po 'le cose e ci permetterà di implementare semplicemente sia ClosedRange che Iterable direttamente :

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

Per la nostra implementazione di iterator () , restituiremo una classe ColorIterator che farà il lavoro pesante di passare effettivamente attraverso l'intervallo.

Perché ColorRange implementa ClosedRange interfaccia, dobbiamo implementare il metodo compareTo sulla classe CustomColor :

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

5. ColorIterator Class

ColorIterator è l'ultimo pezzo del 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++ } }

Notare che initValue è di tipo CustomColor. Quindi, per modificarlo con l' operatore ++ , dovremo aggiungere anche il metodo inc () a CustomColor :

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

6. Utilizzo dell'intervallo personalizzato

Ci siamo quasi!

Poiché stiamo definendo il nostro intervallo personalizzato, la classe CustomColor deve implementare il metodo rangeTo . Il metodo rangeTo ci permetterà di iterare sul nostro range usando l' operatore .. , un po 'come l'aggiunta di inc ci permette di usare l' operatore ++ .

Diamo un'occhiata al prodotto finale:

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) } }

Ed è tutto ciò di cui abbiamo bisogno!

Infine, vediamo come funziona tutto insieme, utilizzando una gamma della nostra 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 } }

In questo test, abbiamo definito una variabile di intervallo e utilizzato per iterare attraverso gli oggetti CustomColor , oltre a trasformarlo in un elenco.

Vediamo un altro esempio di utilizzo del metodo standard contains sull'intervallo:

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

7. Conclusione

Kotlin ha un'implementazione nativa dell'intervallo per i valori Int, Long e Char . In questo articolo abbiamo imparato come implementare un intervallo su una classe personalizzata.

Come sempre, il codice è disponibile su GitHub.