
This is a chapter from the book Kotlin Essentials. You can find it on LeanPub or Amazon. It is also available as a course.
// Java
for(int i=0; i < 5; i++){
System.out.println(i);
}
> or <= instead of <. Such a small difference is not easy to notice, but it essentially influences the behavior of this for-loop.for keyword. Kotlin has simplified this. In Kotlin, we have one universal for-loop that can be expressively used to iterate over a collection, a map, a range of numbers, and much more.
fun main() { val list = listOf("A", "B", "C") for (letter in list) { print(letter) } // Variable type can be explicit for (str: String in setOf("D", "E", "F")) { print(str) } } // ABCDEF
iterator method with no parameters, plus the Iterator result type and the operator modifier. The easiest way to define this operator method is to make your class implement the Iterable interface (then you do not need to define operator modifier yourself, as it is inherited from Iterable). In the below example, we define a Tree class that implements Iterable<String>, so we must override the iterator method, and we can iterate over an instance of this class.fun main() { val tree = Tree( value = "B", left = Tree("A"), right = Tree("D", left = Tree("C")) ) for (value in tree) { print(value) // ABCD } } class Tree( val value: String, val left: Tree? = null, val right: Tree? = null, ) : Iterable<String> { override fun iterator(): Iterator<String> = iterator { if (left != null) yieldAll(left) yield(value) if (right != null) yieldAll(right) } }
Iterable type argument. When we iterate over Iterable<User>, the inferred element type will be User. When we iterate over Iterable<Long?>, the inferred element type will be Long?. The same applies to all other types.Iterable, is really powerful and allows us to cover numerous use cases, one of the most notable of which is the use of ranges to express progressions.1..5, you create an IntRange. This class implements Iterable<Int>, so we can use it in a for-loop:fun main() { for (i in 1..5) { print(i) } } // 12345
.. include the last value (which means they are closed ranges). If you want a range that stops before the last value, use the ..< operator or until infix function instead.fun main() { for (i in 1..<5) { print(i) } } // 1234
fun main() { for (i in 1 until 5) { print(i) } } // 1234
.. and ..< start with the value on their left and progress toward the right number in increments of one. If you use a bigger number on the left, the result is an empty range.fun main() { for (i in 5..1) { print(i) } for (i in 5..<1) { print(i) } } // (nothing is printed)
downTo function.fun main() { for (i in 5 downTo 1) { print(i) } } // 54321
1. If you want to use a different step, you should use the step infix function.fun main() { for (i in 1..10 step 3) { print("$i ") } println() for (i in 1..<10 step 3) { print("$i ") } println() for (i in 10 downTo 1 step 3) { print("$i ") } } // 1 4 7 10 // 1 4 7 // 10 7 4 1
break and continue keywords:break- terminates the nearest enclosing loop.continue- proceeds to the next step of the nearest enclosing loop.
fun main() { for (i in 1..5) { if (i == 3) break print(i) } // 12 println() for (i in 1..5) { if (i == 3) continue print(i) } // 1245 }
fun main() { val names = listOf("Alex", "Bob", "Celina") for (i in 0..<names.size) { val name = names[i] println("[$i] $name") } } // [0] Alex // [1] Bob // [2] Celina
0..<names.size, we could use the indices property, which returns a range of available indices.fun main() { val names = listOf("Alex", "Bob", "Celina") for (i in names.indices) { val name = names[i] println("[$i] $name") } } // [0] Alex // [1] Bob // [2] Celina
withIndex on iterable. Each indexed value includes both an index and a value. Such objects can be destructured in a for-loop[^07_2].fun main() { val names = listOf("Alex", "Bob", "Celina") for ((i, name) in names.withIndex()) { println("[$i] $name") } } // [0] Alex // [1] Bob // [2] Celina
forEachIndexed, which is explained in the next book: Functional Kotlin.fun main() { val names = listOf("Alex", "Bob", "Celina") names.forEachIndexed { i, name -> println("[$i] $name") } } // [0] Alex // [1] Bob // [2] Celina
fun main() { val capitals = mapOf( "USA" to "Washington DC", "Poland" to "Warsaw", "Ukraine" to "Kiev" ) for (entry in capitals.entries) { println("The capital of ${entry.key} is ${entry.value}") } } // The capital of USA is Washington DC // The capital of Poland is Warsaw // The capital of Ukraine is Kiev
entries is unnecessary. Also, we can destructure entries to better name the values.fun main() { val capitals = mapOf( "USA" to "Washington DC", "Poland" to "Warsaw", "Ukraine" to "Kiev" ) for ((country, capital) in capitals) { println("The capital of $country is $capital") } } // The capital of USA is Washington DC // The capital of Poland is Warsaw // The capital of Ukraine is Kiev
forEach for a map.fun main() { val capitals = mapOf( "USA" to "Washington DC", "Poland" to "Warsaw", "Ukraine" to "Kiev" ) capitals.forEach { (country, capital) -> println("The capital of $country is $capital") } } // The capital of USA is Washington DC // The capital of Poland is Warsaw // The capital of Ukraine is Kiev
iterator operator method.[^07_2]: Destructuring will be explained in more depth in the Data classes chapter.
