Перейти к основному содержимому

Синтаксис и пунктуация в Kotlin

Разработчику Архитектору

Откуда взялся синтаксис

По описанию языка, синтаксис — синтез двух ветвей:

  • от C, C++, Java — блоки кода в фигурных скобках, привычная императивная структура;
  • от ML (через Scala) — постфиксные типы (имя — Тип), ключевые слова fun и val, вывод типов.

Точка с запятой не обязательна (как в Scala, Groovy и JavaScript): в большинстве случаев достаточно перевода строки. Поддерживаются процедурный стиль с функциями и ООП. Точка входа — fun main, в том числе с массивом аргументов командной строки. Строки допускают интерполяцию ("Hello, $name!"), как в Perl и shell.

Типичный минимальный пример (как в официальных материалах):

fun main() {
val scope = "world"
println("Hello, $scope!")
}

Работа с nullable — оператор Элвиса и цепочки safe call:

fun sayHello(maybe: String?, neverNull: Int) {
val name: String = maybe ?: "stranger"
println("Hello, $name")
}

// null, если любое звено цепочки null
val result = foo?.bar()?.baz()

Подробнее о типах — Типы данных, о nullсправочник.


Названия знаков по-английски и по-русски: Знаки препинания и символы в IT.

Знаки препинания

Два важных вопроса, которые мучают начинающих программистов:

  1. Когда использовать кавычки двойные ("), одинарные ('), а когда апострофы ()?
  2. Когда использовать точки (.), запятые (,) и точку с запятой (;)?

Двойные (") — обычные строки:

val name = "Alice"

Разбор:

  • Фрагмент показывает исполняемый сценарий: здесь важен порядок шагов и состояние между вызовами.
  • val фиксирует ссылку после инициализации и делает поведение участка более предсказуемым.
  • Операции присваивания связывают вычисленные значения с переменными и делают промежуточные шаги явно читаемыми.
  • Фрагмент полезно читать сверху вниз: каждая строка подготавливает контекст для следующего шага.
  • В практическом коде этот шаблон обычно оборачивают обработкой ошибок и проверкой входных данных.

Тройные (""") — многострочные строки:

val text = """Line 1
Line 2"""

Разбор:

  • Фрагмент показывает исполняемый сценарий: здесь важен порядок шагов и состояние между вызовами.
  • val фиксирует ссылку после инициализации и делает поведение участка более предсказуемым.
  • Операции присваивания связывают вычисленные значения с переменными и делают промежуточные шаги явно читаемыми.
  • Фрагмент полезно читать сверху вниз: каждая строка подготавливает контекст для следующего шага.
  • В практическом коде этот шаблон обычно оборачивают обработкой ошибок и проверкой входных данных.

Интерполяция внутри двойных кавычек:

val age = 25
println("Age: $age")

Разбор:

  • Фрагмент показывает исполняемый сценарий: здесь важен порядок шагов и состояние между вызовами.
  • val фиксирует ссылку после инициализации и делает поведение участка более предсказуемым.
  • println используется как быстрая проверка результата и помогает увидеть фактический ход выполнения.
  • Интерполяция ${...} или $name в строках делает вывод компактным и избавляет от ручной конкатенации.
  • Ключевые вызовы во фрагменте (println) формируют основной поток выполнения и обмена данными.
  • Операции присваивания связывают вычисленные значения с переменными и делают промежуточные шаги явно читаемыми.

Одинарные (') — только для одиночного символа (Char):

val letter = 'A'

Разбор:

  • Фрагмент показывает исполняемый сценарий: здесь важен порядок шагов и состояние между вызовами.
  • val фиксирует ссылку после инициализации и делает поведение участка более предсказуемым.
  • Операции присваивания связывают вычисленные значения с переменными и делают промежуточные шаги явно читаемыми.
  • Фрагмент полезно читать сверху вниз: каждая строка подготавливает контекст для следующего шага.
  • В практическом коде этот шаблон обычно оборачивают обработкой ошибок и проверкой входных данных.

Апострофы () — не поддерживаются. Точка (.) используется для вызова методов и свойств:

val list = listOf(1, 2, 3)
println(list.size)

Разбор:

  • Фрагмент показывает исполняемый сценарий: здесь важен порядок шагов и состояние между вызовами.
  • val фиксирует ссылку после инициализации и делает поведение участка более предсказуемым.
  • println используется как быстрая проверка результата и помогает увидеть фактический ход выполнения.
  • Ключевые вызовы во фрагменте (listOf, println) формируют основной поток выполнения и обмена данными.
  • Операции присваивания связывают вычисленные значения с переменными и делают промежуточные шаги явно читаемыми.
  • В практическом коде этот шаблон обычно оборачивают обработкой ошибок и проверкой входных данных.

Запятая (,) для разделения параметров, элементов списка и т. д.:

val numbers = listOf(1, 2, 3)
fun greet(name: String, age: Int)

Разбор:

  • Фрагмент показывает исполняемый сценарий: здесь важен порядок шагов и состояние между вызовами.
  • fun выделяет именованные блоки логики, что упрощает повторное использование и тестирование кода.
  • val фиксирует ссылку после инициализации и делает поведение участка более предсказуемым.
  • Ключевые вызовы во фрагменте (listOf, greet) формируют основной поток выполнения и обмена данными.
  • Операции присваивания связывают вычисленные значения с переменными и делают промежуточные шаги явно читаемыми.
  • В практическом коде этот шаблон обычно оборачивают обработкой ошибок и проверкой входных данных.

Точка с запятой (;) не требуется в Kotlin. Используется только если нужно писать несколько выражений в одной строке:

val x = 5; val y = 10

Разбор:

  • Фрагмент показывает исполняемый сценарий: здесь важен порядок шагов и состояние между вызовами.
  • val фиксирует ссылку после инициализации и делает поведение участка более предсказуемым.
  • Операции присваивания связывают вычисленные значения с переменными и делают промежуточные шаги явно читаемыми.
  • Фрагмент полезно читать сверху вниз: каждая строка подготавливает контекст для следующего шага.
  • В практическом коде этот шаблон обычно оборачивают обработкой ошибок и проверкой входных данных.

Нижние подчеркивания в Kotlin бывают для стиля и для синтаксиса:

_name - соглашение, но не общепринятое. Приватные поля в Kotlin выглядят как private val logger, а _ используется для полей редко.

_ может быть как игнорирование при деструктуризации:

val (name, _, age) = person

Разбор:

  • Фрагмент показывает исполняемый сценарий: здесь важен порядок шагов и состояние между вызовами.
  • val фиксирует ссылку после инициализации и делает поведение участка более предсказуемым.
  • Ключевые вызовы во фрагменте (val) формируют основной поток выполнения и обмена данными.
  • Операции присваивания связывают вычисленные значения с переменными и делают промежуточные шаги явно читаемыми.
  • Фрагмент полезно читать сверху вниз: каждая строка подготавливает контекст для следующего шага.
  • В практическом коде этот шаблон обычно оборачивают обработкой ошибок и проверкой входных данных.

и как разделитель в числах:

val million = 1_000_000

Разбор:

  • Фрагмент показывает исполняемый сценарий: здесь важен порядок шагов и состояние между вызовами.
  • val фиксирует ссылку после инициализации и делает поведение участка более предсказуемым.
  • Операции присваивания связывают вычисленные значения с переменными и делают промежуточные шаги явно читаемыми.
  • Фрагмент полезно читать сверху вниз: каждая строка подготавливает контекст для следующего шага.
  • В практическом коде этот шаблон обычно оборачивают обработкой ошибок и проверкой входных данных.

Символы "|" и "||" в JavaScript, C#, Java, C++ и Kotlin использутся в общем порядке:

| — это побитовое ИЛИ (bitwise OR): оператор обрабатывает каждый бит целого, в отличие от логического ||.

К примеру, метод(значениеА | значениеБ);

В условиях это логическое ИЛИ, но без сокращённого вычисления.

if (методА() | методБ()) - вызовет и методА, и методБ, даже если методА - true.

|| - логическое ИЛИ (с сокращённым вычислением), можно назвать исключающим.

допустим return a || b - если a true, то b не вернется/не вычислится.

if (a() || b()) { ... } // b() не вызывается, если a() == true

Разбор:

  • Фрагмент показывает исполняемый сценарий: здесь важен порядок шагов и состояние между вызовами.
  • if выполняет проверку условия и направляет выполнение в соответствующую ветку.
  • Ключевые вызовы во фрагменте (a, b) формируют основной поток выполнения и обмена данными.
  • Операции присваивания связывают вычисленные значения с переменными и делают промежуточные шаги явно читаемыми.
  • Границы блоков в фигурных скобках помогают сразу увидеть области видимости и жизненный цикл локальных переменных.
  • В практическом коде этот шаблон обычно оборачивают обработкой ошибок и проверкой входных данных.

Дополнительные сниппеты

val user = "admin"
val role = if (user == "admin") "full-access" else "read-only"
println("Role: $role")

Разбор:

  • Первая строка задаёт исходные данные, а вторая вычисляет производное значение через if-выражение.
  • if в Kotlin возвращает результат, поэтому его удобно использовать прямо в присваивании.
  • val role делает вычисленный результат неизменяемым и снижает риск случайного изменения роли.
  • println выводит итог в консоль и помогает быстро проверить поведение условия.
  • Интерполяция $role вставляет значение в строку без ручной сборки через +.
  • Сценарий отражает типовой шаблон: входные данные → вычисление правила → вывод результата.
val text = "kotlin"
val normalized = text.uppercase().replace("K", "C")
println(normalized)

Разбор:

  • Пример показывает цепочку вызовов у строки, где каждый шаг возвращает новое значение.
  • uppercase() переводит строку в верхний регистр, после чего replace() заменяет конкретный символ.
  • Kotlin-строки неизменяемы, поэтому исходная переменная text сохраняет первоначальное значение.
  • Новое значение сохраняется в normalized, что отделяет исходные данные от результата преобразования.
  • Такой стиль делает обработку текста линейной и читаемой: преобразования идут сверху вниз без скрытых эффектов.