Как работает in в циклах и проверках?

В языке Kotlin ключевое слово in используется в двух основных контекстах:

  1. При **итерации в циклах
    **
  2. При проверке принадлежности элемента коллекции, диапазону или промежутку значений

1. in в циклах for

Оператор in позволяет перебирать элементы коллекций, массивов, диапазонов и любых объектов, реализующих интерфейс Iterable или предоставляющих функцию iterator().

Примеры:

val list = listOf("a", "b", "c")
for (item in list) {
println(item)
}

Здесь item in list означает, что переменная item последовательно принимает значения из списка.

Также можно использовать с диапазонами чисел:

for (i in 1..5) {
println(i)
}

Дополнительно существуют формы:

  • until: исключает верхнюю границу
    for (i in 1 until 5) → 1, 2, 3, 4

  • step: задаёт шаг итерации
    for (i in 1..10 step 2) → 1, 3, 5, 7, 9

  • downTo: обратный порядок
    for (i in 5 downTo 1) → 5, 4, 3, 2, 1

Цикл for (x in collection) трансформируется компилятором в вызовы:

val iterator = collection.iterator()
while (iterator.hasNext()) {
val x = iterator.next()
...
}

2. in в проверках if, when, выражениях

Оператор in может использоваться для проверки, содержится ли элемент в диапазоне (IntRange), коллекции (List, Set, Map.keys) или массиве (Array).

Примеры с диапазоном:

val x = 5
if (x in 1..10) {
println("x попадает в диапазон от 1 до 10")
}

Проверка на отсутствие:

if (x !in 10..20) {
println("x не входит в диапазон 10..20")
}

Примеры с коллекцией:

val names = listOf("Alice", "Bob", "Charlie")
if ("Bob" in names) {
println("Bob найден в списке")
}

Работает через вызов contains():
element in collection компилируется как collection.contains(element)

С when:

when (x) {
in 1..5 -> println("x в первой половине десятка")
in 6..10 -> println("x во второй половине десятка")
!in 1..10 -> println("x вне диапазона 1..10")
}

Как in работает под капотом

Оператор in не является оператором в классическом понимании. Это синтаксический сахар, который Kotlin превращает в вызовы определённых функций:

  • in collection → collection.contains(element)

  • in range → range.contains(value)

  • for (x in iterable) → iterable.iterator() + hasNext() + next()

Для кастомных классов можно переопределить поведение in, реализовав метод operator fun contains(...):

class Box(val items: List<String>) {
operator fun contains(item: String): Boolean {
return items.contains(item)
}
}
val box = Box(listOf("apple", "banana"))
if ("banana" in box) {
println("Есть банан")
}

Где ещё in может использоваться

  • Фильтрация в when:
val grade = 'A'
when (grade) {
in 'A'..'C' -> println("Хорошо")
in 'D'..'F' -> println("Плохо")
else -> println("Неизвестно")
}
  • В DSL (Domain Specific Language) конструкциях:
    Некоторые библиотеки или фреймворки могут использовать in для выражения логики включения, если правильно перегрузить contains.

Различия in и аналогов в Java

В Java нет оператора in. Вместо x in list приходится писать list.contains(x). Для циклов по коллекциям Java использует for-each:

for (String s : list) { ... }

Kotlin делает API выразительнее и безопаснее, сокращая шаблонный код, а оператор in обеспечивает более декларативный и читаемый стиль.