编程后端开发者

描述Kotlin中数值范围的机制:Range和Progression是如何工作的,如何创建自定义范围,以及在哪些场景中它们方便应用?

用 Hintsage AI 助手通过面试

回答

问题的历史

从一开始,Kotlin就引入了范围(Range),以简化对连续数值集的操作——这继承了在处理数字时具有简洁语法的语言(例如Python)。此外,Range机制扩展到Progression,允许指定迭代步长并支持不同类型的数字和字符。

问题

迭代整数、字母、时间点等通常需要特定的语法和标准库的可靠支持,否则代码会变得臃肿、难以阅读且容易在边界发生错误。

解决方案

在Kotlin中,有标准类型IntRange、CharRange和LongRange,以及用于步进迭代的Progression。此外,可以为任意可比较类型定义范围。

// 简单范围 for (i in 1..5) print(i) // 12345 // 设定步长的范围 for (i in 1..10 step 2) print(i) // 13579 // 反向范围 for (i in 5 downTo 1) print(i) // 54321 // 自定义范围(例如,Version) data class Version(val major: Int, val minor: Int): Comparable<Version> { override fun compareTo(other: Version): Int = compareValuesBy(this, other, Version::major, Version::minor) } operator fun ClosedRange<Version>.iterator(): Iterator<Version> = object : Iterator<Version> { var current = start override fun hasNext() = current <= endInclusive override fun next() = current.also { current = Version(current.major, current.minor + 1) } } val v1 = Version(1, 0) val v2 = Version(1, 3) for (v in v1..v2) println(v)

关键特性:

  • 标准范围语法:.., downTo, until, step
  • 支持数值、字符、自定义类型
  • 用于循环、归属检查、分割、验证

经典问题。

1..5与1 until 5有什么区别?

1..5包括范围的两个端点:1,2,3,4,5。1 until 5不包括最后一个元素:1,2,3,4。

可以使用step指定步长小于零的范围吗?

不可以。对于递减范围,请使用downTo结构,然后是step: 5 downTo 1 step 2(得到5,3,1)。

可以使用不实现Comparable的类型来定义范围吗?

不可以。定义范围需要类型支持比较。否则,编译器将不允许定义。

常见错误和反模式

  • 使用until替代..或反之,混淆边界的包含性
  • 为递增范围设置负步长(step不会使范围反向)
  • 不遵循自定义类型在范围内的Comparable要求

生活实例

负面案例

在代码中使用1 until n+1时代替了1..n。得到一个多余的元素,循环超出了允许的范围。

优点:

  • 无意中捕获到边缘情况

缺点:

  • 在分割时发生错误,边界的总和计算错误

正面案例

使用for (i in 0 until n)来索引长度为n的数组,范围严格符合有效的索引值。

优点:

  • 排除了数组越界错误
  • 提高了可读性

缺点:

  • 在不同语言间转换时需要记住..与until之间的区别