Kolejny mały temat, zanim powrócimy do funkcji. Jak wiemy, Kotlin pozwala na przeciążanie operatorów. Jednym z nich jest operator podwójnej kropki – .., który służy do stworzenia zakresu. A do czego może posłużyć nam zakres? Na przykład do uproszczenia zapisu w pętli:

Listing 1. Wykorzystanie zakresu Int

fun main(args: Array<String>) {
    for (i in 1..5)
        println(i)
}

Własne zakresy

Oczywiście możemy utworzyć własny zakres. W tym celu należy przeciążyć operator .. oraz utworzyć klasę rozszerzającą ClosedRange. Dodatkowo klasa, która będzie wyposażona w zakres musi implementować Comparable.

Listing 2. Przygotowanie klasy Version do działania w zakresach

data class Version(val v: Int) : Comparable<Version> {
    override fun compareTo(other: Version): Int {
        return this.v.compareTo(other.v)
    }
}

Kolejnym krokiem jest dodanie odpowiedniego operatora.

Listing 3. Dodanie operatora .. do klasy Version

operator fun Version.rangeTo(b: Version): VersionRange {
    return VersionRange(this, b)
}

W tym miejscu wiemy już, że potrzebujemy klasy VersionRange, która będzie implementować ClosedRange.

Listing 4. Klasa VersionRange

class VersionRange(override val start: Version, override val endInclusive: Version) : ClosedRange<Version>

W ten sposób mamy już skonfigurowany nasz zakres. Jednak nadal nie możemy użyć go w pętli. Sam zakres opisuje tylko pewien zbiór wartości. Nie mówi nic o tym, jak należy się w nim poruszać. W tym celu należy zaimplementować interfejs Iterable:

Listing 5. Klasa VersionRange

class VersionRange(override val start: Version, override val endInclusive: Version) : ClosedRange<Version>, Iterable<Version>{

    override fun iterator(): Iterator<Version> = VersionIterator(start, endInclusive)

}

Implementacja interfejsu pociąga za sobą konieczność stworzenia klasy VersionIterator:

Listing 6. Klasa VersionIterator

class VersionRange(override val start: Version, override val endInclusive: Version) : ClosedRange<Version>, Iterable<Version>{

    override fun iterator(): Iterator<Version> = VersionIterator(start, endInclusive)

    private class VersionIterator(first: Version, last: Version) : Iterator<Version> {
        private var next = first
        private val finalElement = last
        private var hasNext: Boolean = first 
<p>Gotowe. By móc wykorzystać <samp>++</samp> musieliśmy jeszcze dodać przeciążenie tego operatora, ale to już tylko czynność ekstra, by uprościć zapis. Możemy teraz napisać:</p>
<p class="listing">Listing 7. Przykładowe użycie</p>kotlin
fun main(args: Array<String>) {
    for (v in Version(1)..Version(5))
        println(v)
}
<h4>Podsumowanie</h4>
<p>Tworzenie własnych zakresów bywa przydatne. Szczególnie tam gdzie klasy domenowe mają naturalną właściwość do bycia zakresami. Wszelkiej maści wersje, numery seryjne, numery dokumentów  świetnymi kandydatami do tej operacji.</p>
<h4>PS</h4>
<p>Iterator można zaimplementować z wykorzystaniem <samp>AbstractIterator</samp>:</p>
<p class="listing">Listing 8. Wykorzystanie <samp>AbstractIterator</samp></p>kotlin
class VersionRange(override val start: Version, override val endInclusive: Version) : ClosedRange<Version>, Iterable<Version> {

    override fun iterator(): Iterator<Version> = VersionIterator(start, endInclusive)

    private class VersionIterator(var first: Version, val last: Version) : AbstractIterator<Version>() {
        override fun computeNext() {
            if(first > last)
                return done();
            setNext(first++)
        }
    }
}
<p>Czyni to kod bardziej zwięzłym. </p>