ProgrammingKotlin Developer, Backend Developer

How does the mechanism of numeric ranges (Range and Progression) work in Kotlin, how to create custom ranges and for what tasks to use them?

Pass interviews with Hintsage AI assistant

Answer.

Ranges (Range) and progressions (Progression) are a built-in mechanism in Kotlin for representing sequences of values with a specific step. They are often used when working with loops, conditions, iterating over collections, and data validation. Ranges appeared as one of the ways to make Kotlin's syntax more concise and expressive compared to Java.

Background

In Java, similar tasks were handled through for and while loops with indices — verbose and error-prone. In Kotlin, compact operators for creating ranges (e.g., 1..10) and methods for setting steps have been introduced.

Problem

  • The need to easily pass a range of values (for example, all numeric values from 1 to 100).
  • Improved convenience for iteration and validation.
  • The ability to redefine ranges for user-defined types.

Solution

Kotlin provides standard numeric ranges (IntRange, LongRange, CharRange, UIntRange, etc.) and interfaces for creating custom progressions:

Code example:

for (i in 1..5) print("$i ") // 1 2 3 4 5 for (i in 5 downTo 1 step 2) print("$i ") // 5 3 1 // Value check val x = 42 if (x in 1..100) println("In range!")

Custom Ranges

You can define a range for your types by implementing the rangeTo and Progression operators:

data class Version(val major: Int, val minor: Int) : Comparable<Version> { override fun compareTo(other: Version) = compareValuesBy(this, other, Version::major, Version::minor) } operator fun Version.rangeTo(other: Version) = VersionRange(this, other) class VersionRange( override val start: Version, override val endInclusive: Version ) : ClosedRange<Version> for (v in Version(1, 0)..Version(1, 2)) println(v)

Key features:

  • Compact syntax for creating ranges (start..end, downTo, step).
  • Built-in membership checks (in, !in).
  • The ability to define ranges and progressions for user-defined types.

Trick Questions.

What does the expression 1..5 actually return?

It creates an instance of the IntRange class, which implements the ClosedRange<Int> interface. This is not a collection, but an object that defines boundaries and a step. Lazy implementation.

Why is the step of Range always 1? How to change the step?

By default, the step of a range is 1 (or -1 when downTo). For a different step, the step and downTo methods are used. For example:

for (i in 2..10 step 2) println(i)

Can ranges be used with types that do not implement Comparable?

No, for a custom range to work correctly, the type must implement the Comparable interface, otherwise the rangeTo operator will be impossible.

Common mistakes and anti-patterns

  • Trying to change the step of a Range without using the step method.
  • Using a range with types without Comparable.
  • Confused direction (for example, 5..1 will not yield anything).

Real-life Example

Negative Case

A developer uses a for loop (i in 5..1) without downTo, expecting it to yield "5, 4, 3, 2, 1", but the loop never executes.

Pros:

  • Simple syntax.

Cons:

  • Non-obvious behavior of negative ranges.
  • Easy to confuse for a beginner.

Positive Case

Using progressions with downTo and step for iterating reports with the desired interval, making the code compact and self-documenting.

Pros:

  • Concise style.
  • Unlikely to encounter out-of-bounds errors.

Cons:

  • Must know the nuances of steps and directions.