Введение в Kotlin: От скриптов к объектам

Курс закладывает фундамент синтаксиса Kotlin и принципов объектно-ориентированного программирования. Вы пройдете путь от написания простых выражений до создания иерархии классов, необходимых для разработки под Android.

1. Основы синтаксиса и система типов: переменные, функции и управляющие конструкции

Основы синтаксиса и система типов: переменные, функции и управляющие конструкции

Переход на новый язык программирования часто воспринимается как простое изучение нового синтаксиса: где ставить скобки и как объявлять циклы. Однако Kotlin был создан не просто для того, чтобы писать меньше кода, чем в Java. Он требует смены парадигмы мышления. Одно из главных отличий кроется в переходе от программирования инструкциями (statements) к программированию выражениями (expressions). Этот сдвиг, вместе с жесткой, но умной системой типов, позволяет компилятору ловить архитектурные ошибки еще до запуска приложения.

Переменные и философия неизменяемости

В Kotlin объявление переменных начинается с ключевых слов val или var. Это не просто синтаксический сахар, а фундаментальное архитектурное решение, принуждающее разработчика сразу задумываться о жизненном цикле данных.

val (от слова value*) — переменная, доступная только для чтения. После инициализации ей нельзя присвоить новое значение. var (от слова variable*) — классическая изменяемая переменная.

В идиоматичном коде на Kotlin подавляющее большинство переменных объявляется через val. Это снижает количество побочных эффектов и делает код более предсказуемым, особенно при работе в многопоточной среде, с которой вы неминуемо столкнетесь в Android-разработке.

Важный нюанс, на котором часто спотыкаются разработчики: val гарантирует неизменяемость ссылки, а не самого объекта.

Вывод типов (Type Inference)

Kotlin является статически типизированным языком. Это значит, что тип каждой переменной известен на этапе компиляции. Однако писать типы явно каждый раз не нужно благодаря механизму вывода типов.

Явное указание типа требуется только в тех случаях, когда вы объявляете переменную без немедленной инициализации, или когда хотите сузить тип (например, присвоить конкретную реализацию интерфейсу).

Единая система типов

В отличие от некоторых языков, где существует жесткое разделение между примитивами (например, int, double) и объектами, в Kotlin всё является объектом. Вы можете вызывать функции и свойства прямо у чисел: 25.toString(). Под капотом компилятор Kotlin оптимизирует базовые типы в примитивы JVM для производительности, но на уровне синтаксиса разработчик работает с единой объектной моделью.

!Иерархия базовых типов Kotlin

В этой системе есть три особых типа, которые формируют каркас языка:

  • Any — вершина иерархии. Любой не-nullable тип в Kotlin неявно наследуется от Any. Это аналог Object в Java, но с меньшим количеством встроенных методов (только equals, hashCode и toString).
  • Unit — тип, который имеет ровно одно значение. Он используется для функций, которые не возвращают ничего осмысленного (аналог void). Главное отличие в том, что Unit — это полноценный объект, и его можно передавать как аргумент или возвращать из обобщенных (generic) функций.
  • Nothing — «дно» иерархии типов. У этого типа нет экземпляров. Он используется для указания того, что функция никогда не завершится нормально. Например, если функция всегда выбрасывает исключение или содержит бесконечный цикл.
  • > Если функция возвращает Nothing, компилятор понимает, что код, идущий после вызова этой функции, недостижим, и может выдать соответствующее предупреждение или оптимизировать поток выполнения.

    Функции: от классики до лаконичности

    Функции в Kotlin объявляются с помощью ключевого слова fun. Стандартный синтаксис включает имя, параметры с указанием типов и тип возвращаемого значения.

    Если функция не возвращает полезного значения, тип возвращаемого значения Unit можно опустить:

    Single-Expression функции

    Если тело функции состоит из одного выражения, фигурные скобки и ключевое слово return можно убрать, превратив функцию в однострочник. Компилятор сам выведет тип возвращаемого значения.

    Такой подход делает код чище и поощряет написание небольших, узконаправленных функций.

    Аргументы по умолчанию и именованные аргументы

    В Java для создания функции с разным набором параметров приходилось писать множество перегруженных методов. Kotlin решает эту проблему элегантнее — через параметры по умолчанию.

    Эту функцию можно вызвать тремя способами:

  • fetchUser(42) — используются значения по умолчанию.
  • fetchUser(42, true) — переопределяется forceRefresh.
  • fetchUser(42, true, 5000) — переопределяются все параметры.
  • А если нужно изменить только последний параметр? Здесь на помощь приходят именованные аргументы:

    Именованные аргументы кардинально повышают читаемость кода на месте вызова. Вызов setupWindow(true, false, true) заставляет лезть в документацию. Вызов setupWindow(isFullScreen = true, isResizable = false, showShadow = true) понятен с первого взгляда.

    Управляющие конструкции: Выражения против Инструкций

    Чтобы писать идиоматичный код на Kotlin, необходимо четко понимать разницу между инструкцией (statement) и выражением (expression).

    * Инструкция (Statement) — это действие. Она выполняет какую-то работу, но не возвращает результат. В Java почти все управляющие конструкции (if, switch, for) — это инструкции. * Выражение (Expression) — это вычисление. Оно всегда возвращает какое-то значение, которое можно сохранить в переменную или передать дальше.

    В Kotlin конструкции if и when являются выражениями. Это позволяет избавиться от необходимости создавать изменяемые переменные только для того, чтобы присвоить им значение внутри условия.

    if как выражение

    Сравним два подхода. Классический императивный подход (вынуждающий использовать var):

    Идиоматичный подход Kotlin (использование if как выражения):

    Во втором случае переменная status объявлена как val. Значением всего блока if становится результат последнего выражения в сработавшей ветке. Если ветка состоит из одной строки, фигурные скобки можно опустить: val status = if (responseCode == 200) "Success" else "Error".

    Мощь конструкции when

    Конструкция when в Kotlin — это замена оператору switch из C-подобных языков, но значительно более мощная и гибкая. when также является выражением.

    В отличие от switch, который может проверять только на точное совпадение значений (и требует постоянного использования break, чтобы избежать проваливания в следующие ветки), when умеет проверять типы, вхождение в диапазоны и даже вычислять произвольные логические условия.

    !Интерактивный роутинг в when

    Пример использования when для обработки HTTP-ответа:

    Здесь in 400..499 проверяет, попадает ли значение в указанный диапазон. Ветка else обязательна, если компилятор не может гарантировать, что перечисленные условия покрывают все возможные варианты (исчерпываемость).

    when можно использовать и без аргумента. В этом случае он работает как цепочка if-else if, где каждая ветка — это логическое выражение:

    Циклы и диапазоны

    Для итерации по коллекциям или диапазонам Kotlin предлагает цикл for. Он работает с любым объектом, который предоставляет итератор (имеет функцию iterator()).

    Создание диапазонов (Ranges) в Kotlin встроено на уровне синтаксиса языка.

    Цикл while в Kotlin работает точно так же, как и в большинстве других языков, выполняя блок кода до тех пор, пока условие остается истинным. Однако, благодаря богатому набору функций высшего порядка для работы с коллекциями (которые мы подробно изучим позже), потребность в ручном написании циклов for и while в Kotlin возникает значительно реже, чем в Java.

    Понимание того, как Kotlin работает с переменными, функциями и потоком выполнения, закладывает базу для написания безопасного и предсказуемого кода. Привычка мыслить выражениями, а не инструкциями, и стремление к неизменяемости данных — это первый шаг к архитектуре, в которой ошибки отлавливаются компилятором, а не пользователями вашего приложения.