Erstellen eines Kotlin Range Iterators für ein benutzerdefiniertes Objekt

1. Einleitung

In einem früheren Artikel haben wir gezeigt, wie wir in Kotlin einen Bereich erstellen können und wie einfach es ist, über Int-, Long- und Char- Typen zu iterieren .

Aber was ist, wenn wir über einen benutzerdefinierten Typ iterieren möchten ? Ist es möglich? Die Antwort ist ja! Also lasst uns in den Code springen und sehen, wie.

2. Ein bunter Typ

Stellen wir uns vor, wir haben eine einfache Klasse, die eine RGB-Farbe darstellt:

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

Es wäre schön, über eine Reihe von RGB-Farben iterieren zu können:

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

3. Ein kurzer Blick in IntRange

Einfach ausgedrückt, müssen wir implementieren Comparable , Iterable und ClosedRange. Aus unserem vorherigen Artikel wissen wir bereits, dass wir Comparable implementieren müssen .

Lassen Sie uns für die beiden anderen Schnittstellen in die IntRange- Klassendeklaration eintauchen , um einige Hinweise zu erhalten:

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

Und dann zeigt die Erklärung von IntProgression , dass sie Iterable implementiert :

public open class IntProgression : Iterable

Wir werden also etwas Ähnliches tun wollen, damit dies funktioniert.

4. ColorRange- Klasse

Erstellen wir wie IntRange eine ColorRange- Klasse.

Für unsere Zwecke überspringen wir auch die Nachahmung von IntProgression, da wir mit einem Standardschritt von 1 einverstanden sind . Dies vereinfacht die Dinge ein wenig und ermöglicht es uns, sowohl ClosedRange als auch Iterable direkt zu implementieren :

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

Für unsere Implementierung von iterator () geben wir eine ColorIterator- Klasse zurück, die das schwere Heben des tatsächlichen Durchlaufens des Bereichs übernimmt.

Weil ColorRange den ClosedRange implementiertSchnittstelle, müssen wir die Umsetzung compareTo Methode Custom Klasse:

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

5. ColorIterator- Klasse

ColorIterator ist das letzte Puzzleteil:

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

Beachten Sie, dass initValue vom Typ CustomColor ist . Um es mit dem ++ - Operator zu mutieren , müssen wir die inc () -Methode auch zu CustomColor hinzufügen :

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

6. Verwenden des benutzerdefinierten Bereichs

Wir sind fast da!

Da wir unsere eigene Palette sind definiert, Custom muss Klasse die Implementierung RangeTo Methode. Mit der rangeTo- Methode können wir unseren Bereich mit dem Operator .. durchlaufen , ähnlich wie durch Hinzufügen von inc der Operator ++ verwendet werden kann.

Schauen wir uns das Endprodukt an:

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

Und das ist alles was wir brauchen!

Lassen Sie uns abschließend anhand eines Bereichs unserer CustomColor- Klasse sehen, wie dies alles zusammenarbeitet :

@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 diesem Test haben wir eine Bereichsvariable definiert und verwendet, um die CustomColor- Objekte zu durchlaufen und in eine Liste umzuwandeln.

Sehen wir uns ein weiteres Beispiel für die Verwendung der Standard- Include- Methode für den Bereich an:

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

7. Fazit

Kotlin hat eine native Implementierung des Bereichs für Int-, Long- und Char- Werte. In diesem Artikel haben wir gelernt, wie ein Bereich in einer benutzerdefinierten Klasse implementiert wird.

Wie immer ist der Code auf GitHub verfügbar.