Kotlin
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Null Safety
-
Section 4: Object-Oriented Kotlin
-
Section 5: Functional Kotlin
-
Section 6: Coroutines
-
Section 7: Collections Deep Dive
-
Section 8: Type System Deep Dive
-
Section 9: Interop and Tooling
-
Section 10: Kotlin DSLs and Patterns
-
Section 11: Testing and Quality
-
Section 12: Server-Side Kotlin
-
Section 13: Practical Projects
-
Section 14: Interview Practice
-
Section 15: More Practice Exercises
-
Section 16: More Standard Library
-
Section 17: Multiplatform Kotlin
-
Section 18: kotlin.collections In Depth
-
Section 19: kotlin.text In Depth
-
Section 20: kotlin.ranges and kotlin.sequences
-
Section 21: kotlin.io and File Handling
-
Section 22: kotlinx.coroutines Deep Dive
-
Section 23: kotlin.reflect
-
Section 24: Android Development with Kotlin Overview
-
Section 25: Kotlin Multiplatform Deep Dive
-
Section 26: Kotlin for Backend Deep Dive
-
Section 27: Kotlin Design Patterns
-
Section 28: Advanced Language Features
-
Section 29: More Practice Exercises
-
Section 30: More Interview Practice
-
Section 31: Kotlin Type System Deep Dive
-
Section 32: Kotlin Null Safety Advanced
-
Section 33: Kotlin Testing Deep Dive
-
Section 34: Kotlin Build Tooling Deep Dive
-
Section 35: Kotlin Serialization
-
Section 36: Kotlin Performance Considerations
-
Section 37: Kotlin Native Overview
-
Section 38: Kotlin for Data and Scripting
-
Section 39: More Coroutines Practice
-
Section 40: More Android-Adjacent Patterns
-
Section 41: More Practical Projects
-
Section 42: More Design and Architecture Practice
-
Section 43: Kotlin Language Evolution
-
Section 44: More Interview and Review
-
Section 45: Kotlin Delegation Patterns Deep Dive
-
Section 46: Kotlin Annotations Deep Dive
-
Section 47: Kotlin for Gradle Plugin Development
-
Section 48: Kotlin Concurrency Beyond Coroutines
-
Section 49: Kotlin Compiler Internals
-
Section 50: Real-World Kotlin Case Studies
-
Section 51: Final Practice Projects
-
Section 52: Kotlin for Server-Side Reactive Programming
-
Section 53: More Practice and Drills
-
Section 54: Kotlin Security Practices
8: Ranges and Progressions
Ranges in Kotlin are more than just a shorthand for writing loops; they're a first-class way to express "a span of values." I find myself using them constantly for validation and slicing data. To show you how they actually work in a project, let's build a simple Reward System for a game. We want to give players different titles based on their level and print a countdown when they're approaching a boss fight.
Mapping levels to player titles
The most common way to create a range is using the .. operator. This creates a closed range, meaning both the start and the end are included. I like using this with the in operator because it reads almost like a sentence in English.
fun getPlayerTitle(level: Int): String {
return if (level in 1..10) {
"Novice"
} else if (level in 11..30) {
"Warrior"
} else if (level in 31..100) {
"Legend"
} else {
"Unknown"
}
}
It's clean, right? No more clunky level >= 1 && level <= 10 logic. If you need to check the opposite, you can just use !in to see if a value falls outside a range.
The "Off-by-One" trap
Here is where I usually trip up when I'm moving fast. Let's say I have a list of reward descriptions and I want to iterate through them using a range to print them out. I'll try to use the standard .. operator.
val rewards = listOf("Bronze Sword", "Silver Shield", "Golden Armor")
for (i in 0..rewards.size) {
println("Reward ${i + 1}: ${rewards[i]}")
}
If you ran this, your program would crash with an IndexOutOfBoundsException. Why? Because rewards.size is 3, and 0..3 includes the number 3. But since lists are zero-indexed, the last valid index is 2. I forgot that .. is inclusive.
To fix this, I should use the until keyword. It creates a range that excludes the end element. Here is the corrected version:
for (i in 0 until rewards.size) {
println("Reward ${i + 1}: ${rewards[i]}")
}
Counting down and skipping steps
Sometimes you don't want to go up by one. If we're approaching a boss fight at level 50, we might want to warn the player every 5 levels as they get closer. For this, we use downTo for reverse ranges and step to change the increment.
I'll write a quick function that simulates a "Boss Warning" sequence starting from level 50 and counting down to 30, but only hitting every 5th level.
fun printBossWarnings() {
println("Prepare yourself!")
for (level in 50 downTo 30 step 5) {
println("Warning: Boss territory starts at level $level")
}
}
The step modifier is incredibly useful for pagination or processing every Nth item in a dataset. Just remember that downTo is necessary because 50..30 would actually result in an empty range—Kotlin won't automatically assume you want to go backwards just because the first number is larger.
📋 Practical Task
The Tiered Experience Calculator
Build a small program that takes a user's "Experience Points" (XP) as an integer and determines their rank using ranges. Implement the following logic:
- XP 0 to 1000 (inclusive): "Beginner"
- XP 1001 to 5000 (inclusive): "Intermediate"
- XP 5001 to 10000 (inclusive): "Expert"
- Anything above 10000: "Grandmaster"
Additionally, use a for loop with a step to print a "Milestone Check" message for every 2000 XP from 0 up to and including 10000 (e.g., 0, 2000, 4000...).
There are no comments for now.