Переменные и типы
Зачем это нужно
Любая программа что-то запоминает: очки игрока, имя пользователя, список покупок. Место, где хранится такое значение, называется переменной (variable) — это просто имя, за которым компьютер закрепляет ячейку памяти. Без переменных пришлось бы писать число или строку заново в каждом месте кода, где они нужны, и при любом изменении править всё вручную. С переменной достаточно один раз задать значение и дальше ссылаться на него по имени — а если значение поменяется, весь код, который на него смотрит, автоматически получит новое.
В Kotlin у каждой переменной есть тип (type) — метка, которая говорит компилятору (compiler — программе, которая переводит твой код в исполняемый), какого рода данные внутри: целое число, текст, список чисел. Тип определяет, какие операции с переменной вообще имеют смысл: строки можно склеивать, числа — складывать, а вот сложить число со строкой напрямую не выйдет.
В этой главе мы разберём объявление переменной буквально по символам: что значит каждый знак, почему он стоит именно там и что произойдёт, если его убрать. Ничего не считаем очевидным — если знак есть на экране, у него есть причина там быть.
Как это было
Первое объявление: val x = 5
Вот самая короткая осмысленная строка кода с переменной. Наведи курсор (или пройдись Tab с клавиатуры) на каждую часть — появится подсказка.
Что означает каждый символ
val— ключевое слово (keyword): слово, зарезервированное языком. Назвать переменнуюvalнельзя — Kotlin считает его командой «объяви неизменяемую переменную».- пробел — не украшение, а разделитель:
valxкомпилятор прочитал бы как одно непонятное имя. x— имя (identifier). Правила простые: латинские буквы, цифры (не первым символом), без пробелов. Хорошее имя говорит, что внутри:scoreлучше, чемs.=— оператор присваивания (assignment operator). Читается не «икс равен пяти», а «в икс положить пять». Сначала вычисляется всё справа от=, потом результат сохраняется в имя слева.5— литерал (literal): значение, написанное буквально. По нему Kotlin сам поймёт, что тип переменной — целое число.
Теперь запустим это в настоящей программе. Нажми Run — код выполнится прямо в браузере.
fun main() {
// это комментарий: компилятор пропускает всё после двух косых черт
val x = 5
println(x)
}
Разбор по строкам
- Строка 1:
fun main() {— точка входа.fun— ключевое слово «функция» (function): именованный кусок кода, который можно запустить.main— особое имя: именно с этой функции начинается любая программа на Kotlin. Круглые скобки( )— место для входных данных функции; здесь они пустые, потому чтоmainничего не принимает. Открывающая фигурная скобка{начинает тело функции — список команд, которые выполнятся. - Строка 2:
// это комментарий...— две косые черты//начинают комментарий (comment): всё от них до конца строки компилятор игнорирует. Комментарии пишут для людей — себе и товарищам по команде. - Строка 3:
val x = 5— та самая строка. С этого момента имяxсвязано со значением5. - Строка 4:
println(x)— вызов функцииprintln(читается «принт-лайн», от print line — «напечатай строку и перейди на новую»). В круглых скобках — аргумент (argument): то, что мы передаём функции. Здесь передаёмx, поэтому программа напечатает его значение. - Строка 5:
}— закрывающая фигурная скобка: телоmainзакончилось. Каждой{всегда соответствует своя}.
Что выведет: 5 — и ничего больше. Не x и не val x = 5: println печатает значение, которое лежит за именем, а не само имя.
val и var: что можно менять
val (от value) объявляет неизменяемую (immutable) переменную: значение задаётся один раз, и переприсвоить его нельзя — попытка не пройдёт компиляцию. var (от variable) объявляет изменяемую: перезаписывай сколько угодно.
Хорошее правило для новичка: по умолчанию бери val и переключайся на var только тогда, когда точно знаешь, что значение будет меняться. Так меньше шанс случайно испортить данные, на которые полагается остальной код.
Пощупай разницу руками: попробуй переприсвоить обе ячейки и посмотри, как ответит компилятор.
val xнеизменяемаяvar yизменяемаяval a и val b, — а объект MutableList в памяти один: обе стрелки ведут к нему. Добавь элемент через любую из них.val aссылкаval bссылкаMutableListодин объектval здесь прибивает гвоздями стрелку, а не коробку: перенаправить a на другой список нельзя, а вот содержимое — живёт. Коробка прибита, содержимое меняется.Прежде чем жать Run — сначала предскажи вывод сам: прокрути += в голове и вспомни, что делает $ в кавычках.
fun main() {
val name: String = "Соня"
var score = 0
score += 15
score += 27
println("$name набрала $score очков")
}Что напечатает этот код? Сначала ответь, потом проверяй.
fun main() {
val name: String = "Соня"
var score = 0
score += 15
score += 27
println("$name набрала $score очков")
}
Разбор по строкам
- Строка 2:
val name: String = "Соня"— имя участницы не меняется по ходу игры, поэтомуval. Про двоеточие и словоString— целый раздел чуть ниже. - Строка 3:
var score = 0— очки будут расти, поэтомуvar. Типа нет, но он есть: Kotlin вывелIntсам по значению0. - Строки 4–5:
score += 15— новый символ+=: «прибавь к текущему значению». Это сокращение отscore = score + 15. Работает только сvar— переменнойvalприбавлять было бы некуда. - Строка 6:
println("$name набрала $score очков")— внутри кавычек прячутся знаки$; про них тоже отдельный раздел ниже.
Что выведет: Соня набрала 42 очков — потому что 0 + 15 + 27 = 42, а $name и $score подставили значения переменных прямо в текст.
Тип: метка на коробке
Явная аннотация: val name: String = "Олег"
Тип можно указать вручную — через двоеточие после имени. Разбираем по частям.
Новые символы:
:— двоеточие после имени переменной означает «имеет тип». Записьname: Stringчитается «name имеет тип String»."— двойные кавычки (quotes) ограничивают строку (string) — текст. Всё между ними — значение; сами кавычки в него не входят. Забудешь закрывающую — компилятор потеряет границу текста и откажется работать (покажем в разделе про ошибки).
fun main() {
val name: String = "Олег"
println(name)
}
Что выведет: Олег — без кавычек: они были только границей литерала в коде, частью значения они не являются.
Какие типы бывают
Пять типов, которые встречаются чаще всего. Запусти и посмотри, как печатается каждый.
fun main() {
val age: Int = 16 // целое число
val height: Double = 1.75 // дробное число; разделитель — точка, не запятая
val letter: Char = 'ф' // ровно один символ, в ОДИНАРНЫХ кавычках
val isChampion: Boolean = true // логическое значение: true или false
val team: String = "ПГК" // текст, в двойных кавычках
println(age)
println(height)
println(letter)
println(isChampion)
println(team)
}
Разбор по строкам
Int— целое число (integer): 16, 0, −5. Без дробной части.Double— дробное число:1.75. Дробная часть отделяется точкой — запятая тут вызвала бы ошибку, потому что запятая в Kotlin разделяет аргументы.Char— один символ (character) в одинарных кавычках:'ф'. Одинарные кавычки — для одного символа, двойные — для текста любой длины; перепутать их нельзя, это разные типы.Boolean— логический тип: толькоtrue(истина) илиfalse(ложь). Пишутся без кавычек — это ключевые слова, а не текст.String— уже знакомый текст в двойных кавычках.
Что выведет: пять строк — 16, 1.75, ф, true, ПГК, каждая на своей строке, потому что println после печати переходит на новую.
Вывод типов: почему тип можно не писать
Если справа от = уже стоит значение, Kotlin определит тип сам: увидел 0 — значит Int, увидел "Олег" — значит String. Это называется выводом типов (type inference).
Важно: это не «отсутствие типа» — тип есть всегда, он фиксируется при компиляции раз и навсегда, просто писать его руками не обязательно. Явную аннотацию оставляют, когда без неё код непонятен или когда нужен не тот тип, который выводится по умолчанию.
Строковые шаблоны: $ внутри кавычек
Чтобы подставить значение переменной внутрь текста, не нужно ничего склеивать — достаточно знака доллара.
Новый символ — $ (знак доллара): внутри двойных кавычек он означает «вставь сюда значение». После $ пишут имя переменной, а если нужно целое выражение — берут его в фигурные скобки: ${...}.
fun main() {
val name = "Олег"
val age = 16
println("Привет, $name!")
println("Тебе $age лет")
println("Через год будет ${age + 1}")
}
Разбор по строкам
- Строки 2–3 — объявляем строку и число; типы Kotlin выводит сам.
- Строка 4:
println("Привет, $name!")— печатаетПривет, Олег!: на месте$nameоказалось значение переменной, а весь остальной текст — как был. - Строка 5:
println("Тебе $age лет")— печатаетТебе 16 лет: шаблон работает с любым типом, число превратилось в текст автоматически. - Строка 6:
println("Через год будет ${age + 1}")— печатаетЧерез год будет 17. Здесь после$стоят фигурные скобки: внутри них — целое выражениеage + 1, которое сначала вычисляется, и только потом результат подставляется в строку. Без скобок Kotlin решил бы, что переменная называетсяage, а+ 1— просто текст.
Цепочка вызовов: точка за точкой
Сила Kotlin — в том, что операции можно нанизывать одну за другой. Разберём цепочку, которая удваивает числа в списке и складывает результат.
Новые символы:
.— точка перед методом: «у значения слева вызови операцию справа». Метод (method) — функция, привязанная к значению. После каждой точки цепочка работает уже с результатом предыдущего шага.{ }— фигурные скобки здесь ограничивают лямбду (lambda): маленький безымянный кусок кода, который передают другой функции. Это те же скобки, что и у телаmain, — в Kotlin они всегда ограничивают блок кода.*— умножение.+,-,*,/— четыре арифметических оператора.
fun main() {
val numbers = listOf(1, 2)
val doubled = numbers.map { it * 2 }
val total = numbers.map { it * 2 }.sum()
println(doubled)
println(total)
}
Разбор по строкам
- Строка 2 — создаём список
[1, 2]. Его тип Kotlin выводит сам:List<Int>— «список целых чисел». - Строка 3 —
mapберёт каждый элемент (он попадает вit), умножает на 2 и собирает новый список. Исходныйnumbersне меняется — получается новый список. - Строка 4 — та же цепочка плюс
.sum(): список[2, 4]сворачивается в число6. - Строки 5–6 — печать. Что выведет:
[2, 4], затем6. Списки печатаются в квадратных скобках через запятую — так выглядит их текстовое представление.
Лямбду не обязательно писать через it. Когда так понятнее, параметру дают явное имя и отделяют его стрелкой -> (минус и знак «больше», читается «переходит в»):
И здесь сначала предскажи: подставь 15 вместо score в тело лямбды и посчитай, что напечатается.
fun main() {
val calculateBonus: (Int) -> Int = { score -> score * 2 + 10 }
println(calculateBonus(15))
}Что напечатает этот код? Сначала ответь, потом проверяй.
fun main() {
val calculateBonus: (Int) -> Int = { score -> score * 2 + 10 }
println(calculateBonus(15))
}
Разбор по строкам
- Строка 2 — здесь стрелка встречается дважды в разных ролях. В типе
(Int) -> Intона описывает функцию: «принимает Int, возвращает Int». В лямбде{ score -> score * 2 + 10 }она отделяет имя параметра от тела: «взять score и посчитать score * 2 + 10». - Строка 3 — вызываем лямбду как обычную функцию. Что выведет:
40, потому что 15 × 2 + 10 = 40.
Типичные ошибки
Ошибки компиляции — не провал, а подсказки. Компилятор всегда пишет, что не так и где.
Ниже — четыре ошибки, которые делает каждый новичок. Каждый пример можно запустить и увидеть настоящее сообщение — тексты здесь приведены ровно такими, какими их показывает компилятор.
1. Переприсваивание val
fun main() {
val record = 100
println(record)
record = 120
}
Компилятор откажется собирать программу и укажет на строку 4:
'val' cannot be reassigned.
Перевод: «val нельзя переприсвоить». Переменная объявлена как val, а мы пытаемся положить в неё новое значение. Рядом может появиться и жёлтое предупреждение Assigned value is never read. — «присвоенное значение никогда не читается»; оно вторично, чинить надо красную ошибку.
Как починить: если значение должно меняться — объяви переменную как var record = 100. Если нет — не переприсваивай, а заведи новую: val newRecord = 120.
2. Забытая кавычка
fun main() {
val greeting = "Привет
println(greeting)
}
Syntax error: Expecting '"'.
Перевод: «Синтаксическая ошибка: ожидалась кавычка». Строковый литерал открыли, а закрыть забыли — компилятор дочитал до конца строки, не нашёл вторую " и потерял границу текста.
Как починить: закрыть строку: val greeting = "Привет". Кавычки всегда ходят парами, как и скобки.
3. Несовпадение типов
fun main() {
val points: Int = "5"
println(points)
}
Initializer type mismatch: expected 'Int', actual 'String'.
Перевод: «Несовпадение типа начального значения: ожидался Int, а по факту String». Мы пообещали компилятору целое число (: Int), а положили текст — "5" в кавычках это строка, а не цифра пять.
Как починить: убрать кавычки — val points: Int = 5, — либо, если это действительно текст, поменять тип: val points: String = "5". Обещание и значение должны совпадать.
4. Опечатка в имени
fun main() {
val score = 10
println("Очки: $score")
println(scre)
}
Unresolved reference 'scre'.
Перевод: «Неразрешённая ссылка scre» — компилятор не нашёл ничего с таким именем. Переменной scre не существует: есть score, а в строке 4 — опечатка. Та же ошибка появится, если использовать переменную, которую вообще забыли объявить.
Как починить: исправить имя на существующее — println(score). Компилятор различает каждую букву и даже регистр: Score и score — разные имена.
Теперь охота: в каждом сниппете ровно одна строка не пройдёт компиляцию — найди её с первого клика.
Слова главы
Потренируйся печатать
Сначала короткое объявление — до автоматизма:
Цель: скорость ≥ 90 зн/мин, точность ≥ 90%
val x = 5
Теперь цепочка посложнее — со скобками, стрелкой и лямбдой:
Цель: скорость ≥ 110 зн/мин, точность ≥ 90%
val calculateBonus: (Int) -> Int = { score -> score * 2 + 10 }
Символы главы
Шпаргалка по всем знакам, которые встретились. Вернись сюда, если забудешь, «что это за закорючка».
| Символ | Как называется | Что делает | Пример |
|---|---|---|---|
= | оператор присваивания | вычисляет правую часть и кладёт в переменную слева | val x = 5 |
: | двоеточие (аннотация типа) | после имени переменной указывает её тип | val name: String |
" | двойные кавычки | ограничивают строку — текст любой длины | "Олег" |
' | одинарные кавычки | ограничивают ровно один символ (Char) | 'ф' |
$ | знак шаблона | внутри кавычек подставляет значение переменной | "Привет, $name" |
${ } | шаблон с выражением | подставляет результат целого выражения | "будет ${age + 1}" |
( ) | круглые скобки | вызов функции и передача аргументов | println(x) |
{ } | фигурные скобки | ограничивают блок кода: тело функции или лямбду | { it * 2 } |
. | точка | вызывает метод у значения слева от неё | listOf(1, 2).sum() |
// | двойная косая черта | начинает комментарий до конца строки | // заметка |
+= | присваивание с прибавлением | прибавляет к текущему значению переменной | score += 15 |
* | звёздочка | умножение | it * 2 |
-> | стрелка | отделяет параметры лямбды от её тела | { score -> score * 2 } |
Углубиться
val и var под капотом. Любое свойство в Kotlin — не просто ячейка памяти, а пара функций-аксессоров: геттер (getter), который читает значение, и, только для var, сеттер (setter), который его меняет. Компилятор генерирует их сам, а также backing field — скрытое поле, где значение фактически хранится. Поэтому val — это не «переменная без сеттера» как синтаксический трюк, а тип, у которого сеттера действительно нет: изменить значение снаружи буквально нечем.
Вывод типов. Компилятор смотрит на выражение справа от = и решает, какой тип нужен переменной слева, ещё во время компиляции — то есть до запуска программы. Это не динамическая типизация: тип фиксируется раз и навсегда, просто программисту не обязательно писать его руками каждый раз.
Дальше по теме:
- Basic syntax — Variables — синтаксис val/var и первое знакомство с выводом типов
- Properties — геттеры, сеттеры и backing field: как val/var устроены изнутри
- Lambdas — лямбда-выражения и параметр it подробнее
Куда дальше: проверенные ресурсы
- kotlinlang.ru — Основной синтаксис — официальная документация в переводе: тот же материал, что у авторов языка, но по-русски.
- Урок 1: переменные в Kotlin — пошаговый разбор val/var и типов, идеален как второе прочтение этой главы другими словами.
- Хабр: переменные и типы данных в Kotlin — свежая статья с таблицей типов, toInt и чтением ввода через readln — чуть шире нашей главы.
- Гоша Дударь — видеоурок по переменным — если проще воспринимать на слух и глазами, чем текстом.
- Kotlin tour: Hello world — тур для тех, кто начинает с нуля: маленькие шаги и встроенные упражнения после каждой темы.
- Kotlin tour: Basic types — продолжение тура про типы: закрепишь Int, Double, String, Boolean на упражнениях.
- Basic syntax — компактная шпаргалка по всему синтаксису, к которой удобно возвращаться.
-
Kotlin Koans — интерактивные задачи; новичку стоит брать точечно только Introduction: Hello world и String templates, остальное рассчитано на переходящих с Java.
-
Hyperskill: Kotlin variables — бесплатные упражнения именно на переменные с автопроверкой.
-
Видео и материалы сообщества — ниже — ролики по теме и ссылки от студентов и преподавателей смотри в самом низу страницы.
Челлендж ⭐
Спортивная команда пробежала за неделю пять тренировок: listOf(3, 5, 4, 6, 2) километров.
Ступень 1. Организаторам чемпионата нужна общая дистанция в метрах. Напиши одно выражение — по образцу listOf(...).map { ... }.sum() из этой главы, — которое переводит каждое значение из километров в метры и сразу суммирует.
Ступень 2 ⭐. Тренер просит вторую строку отчёта: среднюю дистанцию за тренировку в километрах, в формате В среднем 4.0 км за тренировку. Подсказка: у списка чисел есть метод .average(), а собрать строку поможет шаблон с $.
fun main() {
val distancesKm = listOf(3, 5, 4, 6, 2)
// ступень 1: список километров -> сумма метров
val totalMeters = 0 // замени 0 на своё выражение
println("Всего пробежали: $totalMeters м")
// ступень 2 (со звёздочкой): выведи среднюю дистанцию через .average()
}
Решение (сначала попробуй сам)
Ступень 1:
val totalMeters = distancesKm.map { it * 1000 }.sum()
Ступень 2 ⭐:
val averageKm = distancesKm.average()
println("В среднем $averageKm км за тренировку")
average() возвращает Double, поэтому напечатается 4.0, а не 4 — дробные числа всегда показывают дробную часть.
Сводная проверка по всей главе: 7 вопросов, 5:00 на всё, без подсказок по ходу. Вопросы показываются по одному, ответ изменить нельзя. Если время выйдет — экзамен завершится с тем, что успел ответить. Пересдавать можно сколько угодно раз.
Что должен уметь
- Объяснять разницу между
valиvarи обосновывать, какой из них выбрать в конкретной ситуации. - Читать объявление переменной как с явной аннотацией типа, так и без неё, и называть, что делает каждый символ в нём:
=,:, кавычки. - Формулировать, что такое вывод типов (type inference) и почему Kotlin не всегда требует писать тип вручную.
- Различать основные типы —
Int,Double,String,Char,Boolean— и объяснять, чем5отличается от"5". - Подставлять значения в текст через строковые шаблоны
$nameи${выражение}. - Читать и строить цепочку
listOf(...).map { ... }.sum(), объясняя, что делает каждое звено и каждая точка. - Использовать в лямбде как неявный параметр
it, так и явно названный параметр со стрелкой->. - Узнавать по сообщению компилятора четыре типичные ошибки — переприсваивание
val, незакрытую кавычку, несовпадение типов, опечатку в имени — и чинить каждую.
Комментарии
Комментарии появятся после настройки. Нужен аккаунт GitHub — вход прямо в виджете выше.