Ranges and progressions
Kotlin lets you easily create ranges of values using the .rangeTo() and .rangeUntil() functions from the kotlin.ranges package.
- a closed-ended range, call the .rangeTo() function with the .. operator.
- an open-ended range, call the .rangeUntil() function with the .. < operator.
Ranges are particularly useful for iterating over for loops:
To iterate numbers in reverse order, use the downTo function instead of .. .
It is also possible to iterate over numbers with an arbitrary step (not necessarily 1). This is done via the step function.
Progression
The ranges of integral types, such as Int , Long , and Char , can be treated as arithmetic progressions. In Kotlin, these progressions are defined by special types: IntProgression , LongProgression , and CharProgression .
Progressions have three essential properties: the first element, the last element, and a non-zero step . The first element is first , subsequent elements are the previous element plus a step . Iteration over a progression with a positive step is equivalent to an indexed for loop in Java/JavaScript.
When you create a progression implicitly by iterating a range, this progression’s first and last elements are the range’s endpoints, and the step is 1.
To define a custom progression step, use the step function on a range.
The last element of the progression is calculated this way:
- For a positive step: the maximum value not greater than the end value such that (last — first) % step == 0 .
- For a negative step: the minimum value not less than the end value such that (last — first) % step == 0 .
Thus, the last element is not always the same as the specified end value.
Progressions implement Iterable , where N is Int , Long , or Char respectively, so you can use them in various collection functions like map , filter , and other.
Conditions and loops
In Kotlin, if is an expression: it returns a value. Therefore, there is no ternary operator ( condition ? then : else ) because ordinary if works fine in this role.
fun main() < val a = 2 val b = 3 //sampleStart var max = a if (a < b) max = b // With else if (a >b) < max = a >else < max = b >// As expression max = if (a > b) a else b // You can also use `else if` in expressions: val maxLimit = 1 val maxOrLimit = if (maxLimit > a) maxLimit else if (a > b) a else b //sampleEnd println(«max is $max») println(«maxOrLimit is $maxOrLimit») >
Branches of an if expression can be blocks. In this case, the last expression is the value of a block:
If you’re using if as an expression, for example, for returning its value or assigning it to a variable, the else branch is mandatory.
When expression
when defines a conditional expression with multiple branches. It is similar to the switch statement in C-like languages. Its simple form looks like this.
when matches its argument against all branches sequentially until some branch condition is satisfied.
when can be used either as an expression or as a statement. If it is used as an expression, the value of the first matching branch becomes the value of the overall expression. If it is used as a statement, the values of individual branches are ignored. Just like with if , each branch can be a block, and its value is the value of the last expression in the block.
The else branch is evaluated if none of the other branch conditions are satisfied.
If when is used as an expression, the else branch is mandatory, unless the compiler can prove that all possible cases are covered with branch conditions, for example, with enum class entries and sealed class subtypes).
enum class Bit < ZERO, ONE >val numericValue = when (getRandomBit()) < Bit.ZERO ->0 Bit.ONE -> 1 // ‘else’ is not required because all cases are covered >
In when statements, the else branch is mandatory in the following conditions:
- when has a subject of a Boolean , enum , or sealed type, or their nullable counterparts.
- branches of when don’t cover all possible cases for this subject.
enum class Color < RED, GREEN, BLUE >when (getColor()) < Color.RED ->println(«red») Color.GREEN -> println(«green») Color.BLUE -> println(«blue») // ‘else’ is not required because all cases are covered > when (getColor()) < Color.RED ->println(«red») // no branches for GREEN and BLUE else -> println(«not red») // ‘else’ is required >
To define a common behavior for multiple cases, combine their conditions in a single line with a comma:
You can use arbitrary expressions (not only constants) as branch conditions
You can also check a value for being in or !in a range or a collection:
when (x) < in 1..10 ->print(«x is in the range») in validNumbers -> print(«x is valid») !in 10..20 -> print(«x is outside the range») else -> print(«none of the above») >
Another option is checking that a value is or !is of a particular type. Note that, due to smart casts, you can access the methods and properties of the type without any extra checks.
when can also be used as a replacement for an if — else if chain. If no argument is supplied, the branch conditions are simply boolean expressions, and a branch is executed when its condition is true:
You can capture when subject in a variable using following syntax:
fun Request.getBody() = when (val response = executeRequest()) < is Success ->response.body is HttpError -> throw HttpException(response.status) >
The scope of variable introduced in when subject is restricted to the body of this when.
For loops
The for loop iterates through anything that provides an iterator. This is equivalent to the foreach loop in languages like C#. The syntax of for is the following:
The body of for can be a block.
As mentioned before, for iterates through anything that provides an iterator. This means that it:
- has a member or an extension function iterator() that returns Iterator<> :
- has a member or an extension function next()
- has a member or an extension function hasNext() that returns Boolean .
All of these three functions need to be marked as operator .
To iterate over a range of numbers, use a range expression:
A for loop over a range or an array is compiled to an index-based loop that does not create an iterator object.
If you want to iterate through an array or a list with an index, you can do it this way:
Alternatively, you can use the withIndex library function:
While loops
while and do-while loops execute their body continuously while their condition is satisfied. The difference between them is the condition checking time:
- while checks the condition and, if it’s satisfied, executes the body and then returns to the condition check.
- do-while executes the body and then checks the condition. If it’s satisfied, the loop repeats. So, the body of do-while executes at least once regardless of the condition.
Break and continue in loops
Kotlin supports traditional break and continue operators in loops. See Returns and jumps.
Условия и циклы
В языке Kotlin if является выражением, т.е. оно возвращает значение. Это позволяет отказаться от тернарного оператора ( условие ? условие истинно : условие ложно ), потому что обычному if вполне по силам его заменить.
// обычное использование var max = a if (a < b) max = b // с блоком else var max: Int if (a >b) < max = a >else < max = b >// в виде выражения val max = if (a > b) a else b
«Ветви» выражения if могут быть блоками, т.е. содержать несколько строк кода, при этом последнее выражение является значением блока:
Если вы используете if в качестве выражения (например, возвращая его значение или присваивая его переменной), то использование ветки else является обязательным.
Условное выражение when
when определяет условное выражение с несколькими «ветвями». Оно похоже на оператор switch , присутствующий в C-подобных языках.
when (x) < 1 ->print("x == 1") 2 -> print("x == 2") else -> < // обратите внимание на блок print("x не равен ни 1, ни 2") >>
when последовательно сравнивает свой аргумент со всеми указанными значениями, пока не выполнится какое-либо из условий ветвей.
when можно использовать и как выражение, и как оператор. При использовании его в виде выражения значение первой ветки, удовлетворяющей условию, становится значением всего выражения. При использовании в виде оператора значения отдельных веток отбрасываются. В точности как if : каждая ветвь может быть блоком и её значением является значение последнего выражения блока.
Значение ветки else вычисляется в том случае, когда ни одно из условий в других ветках не удовлетворено.
Если when используется как выражение, то ветка else является обязательной, за исключением случаев, в которых компилятор может убедиться, что ветки покрывают все возможные значения. Так происходит, например с записями класса enum и с подтипами sealed (изолированных) классов.
enum class Bit < ZERO, ONE >val numericValue = when (getRandomBit()) < Bit.ZERO ->0 Bit.ONE -> 1 // 'else' не требуется, потому что все случаи учтены >
В операторах when ветка else является обязательной в следующих условиях:
- when имеет объект типа Boolean , enum , sealed или их nullable-аналоги;
- ветки when не охватывают все возможные случаи для этого объекта.
enum class Color < RED, GREEN, BLUE >when (getColor()) < Color.RED ->println("red") Color.GREEN -> println("green") Color.BLUE -> println("blue") // 'else' не требуется, потому что все случаи учтены > when (getColor()) < Color.RED ->println("red") // нет веток для GREEN и BLUE else -> println("not red") // 'else' обязателен >
Если для нескольких значений выполняется одно и то же действие, то условия можно перечислять в одной ветке через запятую.
when (x) < 0, 1 ->print("x == 0 or x == 1") else -> print("otherwise") >
Помимо констант в ветках можно использовать произвольные выражения.
when (x) < s.toInt() ->print("s encodes x") else -> print("s does not encode x") >
Также можно проверять вхождение аргумента в интервал in или !in или его наличие в коллекции:
when (x) < in 1..10 ->print("x is in the range") in validNumbers -> print("x is valid") !in 10..20 -> print("x is outside the range") else -> print("none of the above") >
Помимо этого Kotlin позволяет с помощью is или !is проверить тип аргумента. Обратите внимание, что благодаря умным приведениям вы можете получить доступ к методам и свойствам типа без дополнительной проверки.
fun hasPrefix(x: Any) = when(x) < is String ->x.startsWith("prefix") else -> false >
when удобно использовать вместо цепочки условий вида if — else if . При отсутствии аргумента условия работают как простые логические выражения, а тело ветки выполняется при его истинности.
when < x.isOdd() ->print("x is odd") y.isEven() -> print("y is even") else -> print("x+y is odd") >
Можно получать переменную внутри when условия по следующему синтаксису:
fun Request.getBody() = when (val response = executeRequest()) < is Success ->response.body is HttpError -> throw HttpException(response.status) >
Такая переменная, объявленная внутри условия when может быть видна только внутри тела этого when .
Цикл for
Цикл for обеспечивает перебор всех значений, поставляемых итератором. Он эквивалентен циклу foreach в таких языках, как C#.
for (item in collection) print(item)
Телом цикла может быть блок кода.
Как отмечено выше, цикл for позволяет проходить по всем элементам объекта, имеющего итератор, например:
- обладающего внутренней или внешней функцией iterator() , возвращаемый тип которой Iterator<> :
- обладает внутренней или внешней функцией next()
- обладает внутренней или внешней функцией hasNext() , возвращающей Boolean .
Все три указанные функции должны быть объявлены как operator .
Чтобы перебрать диапазон чисел, используйте выражение диапазона:
for (i in 1..3) < println(i) >for (i in 6 downTo 0 step 2)
Цикл for по диапазону или массиву компилируется в основанный на индексе цикл, который не создает объект итератора.
Если при проходе по массиву или списку необходим порядковый номер элемента, используйте следующий подход:
Также вы можете использовать библиотечную функцию withIndex .
for ((index, value) in array.withIndex())
Цикл while
Тело циклов while и do-while выполняется до тех пор, пока их условие выполняется. Разница между ними заключается во времени проверки условия:
- while проверяет условие и, если оно истинно, выполняет тело, а затем возвращается к проверке условия;
- do-while выполняет тело и только затем проверяет условие. Если оно выполняется, цикл повторяется. Таким образом, тело do-while выполняется по крайней мере один раз независимо от условия.
while (x > 0) < x-- >do < val y = retrieveData() >while (y != null) // y здесь доступно!
Break и continue в циклах
Kotlin поддерживает привычные операторы break и continue в циклах. См. Операторы перехода.
© 2015—2023 Open Source Community