Criando um iterador de intervalo Kotlin em um objeto personalizado

Criando um iterador de intervalo Kotlin em um objeto personalizado

1. Introdução

Em um artigo anterior, mostramoshow we can create a range in Kotlin e como é fácil iterar nos tiposInt, LongeChar.

Mas e se quisermositerate over a custom type? É possível? A resposta é sim! Então, vamos pular para o código e ver como.

2. Um tipo colorido

Vamos imaginar que temos uma classe simples que representa uma cor RGB:

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

Seria bom ser capaz de iterar em uma gama de cores RGB:

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

3. Uma rápida olhada emIntRange

Simplificando,we’ll need to implement ComparableIterable, and ClosedRange. Deour previous article, já sabemos que teremos que implementarComparable.

Para as outras duas interfaces, vamos mergulhar na declaração de classeIntRange para algumas dicas:

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

E então, a declaração deIntProgression mostra que ele implementaIterable<Int>:

public open class IntProgression : Iterable

Então, vamos querer fazer algo semelhante para fazer isso funcionar.

4. ClasseColorRange

ComoIntRange, vamos criar uma classeColorRange.

Para nossos propósitos, vamos pular a imitação deIntProgression, também, já quewe’re okay with having a default step of 1. isso simplificará um pouco as coisas e nos permitirá simplesmenteimplement both ClosedRange and Iterable directly:

class ColorRange(override val start: CustomColor,
  override val endInclusive: CustomColor) : ClosedRange, Iterable{

    override fun iterator(): Iterator {
        return ColorIterator(start, endInclusive)
    }
}

Para nossa implementação deiterator(), retornaremos umColorIterator class that will do the heavy lifting of actually stepping through the range.

ComoColorRange implementa a interfaceClosedRange<T: Comparable<T>>, temos que implementar o métodocompareTo na classeCustomColor:

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

5. ClasseColorIterator

ColorIterator is the last piece do quebra-cabeça:

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

Observe queinitValue é do tipoCustomColor. Então, para mutá-lo com o operador++, precisaremos adicionar o métodoinc() aCustomColor também :

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

6. Usando o intervalo personalizado

Estamos quase lá!

Como estamos definindo nosso intervalo personalizado, a classeCustomColor deve implementar o métodorangeTo. The rangeTo method will allow us to iterate over our range using the .. operator, mais ou menos como adicionarinc nos permite usar o operador++ .

Vamos verificar o produto 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)
    }
}

E isso é tudo de que precisamos!

Finalmente, vamos ver como tudo isso funciona junto, porusing a range of our CustomColor class:

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

Neste teste, definimos uma variávelrange e usamos para iterar os objetosCustomColor, bem como transformá-la em uma lista.

Vejamos outro exemplo de uso do métodocontains padrão no intervalo:

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

7. Conclusão

Kotlin tem uma implementação nativa de intervalo para valores deInt, LongeChar. Neste artigo, aprendemos como implementar um intervalo em uma classe personalizada.

Como sempre, o código está disponívelon GitHub.