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 Comparable, Iterable, 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.