Files
obsidian/Kotlin/Инструментарий/Инструментарий.md
T

14 KiB
Raw Blame History

title, description, tags
title description tags
Инструментарий Обзор инструментов Kotlin — компилятор, сборка, кодогенерация, статический анализ — и как они складываются в проект
kotlin
tooling

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

graph TD
    A["исходники .kt"] --> B["компилятор K2<br/>kotlinc"]
    B --> C[".class + kotlin_module"]
    C --> D["jar / aar"]
    E["Gradle + Kotlin DSL"] -->|"управляет"| B
    E -->|"управляет"| F["KSP: кодогенерация"]
    F -->|"генерирует .kt"| A
    E -->|"управляет"| G["ktlint / detekt"]
    G -->|"проверяет"| A

Четыре слоя

Слой Инструмент Зачем
компиляция kotlinc / компилятор K2 исходники → байт-код
сборка Gradle + Kotlin DSL зависимости, таски, многомодульность
кодогенерация KSP аннотации → сгенерированный код
качество ktlint, detekt формат и статический анализ
fun main() {
    // этот файл можно скомпилировать тремя способами:
    // 1. kotlinc Main.kt -include-runtime -d app.jar
    // 2. gradle build (с build.gradle.kts)
    // 3. kotlinc -script Main.main.kts (как скрипт)
    println("любой способ даёт один байт-код")
}

Какой инструмент когда

kotlinc напрямую - для одного файла, эксперимента, проверки факта о языке. Не масштабируется: нет управления зависимостями.

Gradle - стандарт для всего остального. Kotlin DSL (build.gradle.kts) - это Kotlin-код, поэтому работает автодополнение и проверка типов. Подробнее: Gradle и Kotlin DSL

Maven поддерживается плагином kotlin-maven-plugin, но экосистема Kotlin ориентирована на Gradle: Multiplatform, Compose и большинство плагинов существуют только для него.

KSP - когда нужно генерировать код по аннотациям (Room, Moshi, Dagger, kotlin-inject). Заменил kapt, который работал через Java-стабы и был в разы медленнее. Подробнее: KSP

Минимальный проект

// build.gradle.kts
plugins {
    kotlin("jvm") version "2.4.0"
    application
}

repositories { mavenCentral() }

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0")
    testImplementation(kotlin("test"))
}

kotlin {
    jvmToolchain(21)
}

application {
    mainClass.set("com.example.MainKt")
}

tasks.test {
    useJUnitPlatform()
}

Четыре блока покрывают 90% нужд: плагины, репозитории, зависимости, версия JVM. Остальное - настройка под конкретную задачу.

Структура каталогов

project/
├── build.gradle.kts          # сборка модуля
├── settings.gradle.kts        # список модулей
├── gradle.properties           # флаги Gradle и JVM
├── gradle/
│   ├── libs.versions.toml      # version catalog
│   └── wrapper/                 # gradle-wrapper.jar + properties
├── gradlew                       # запуск без установленного Gradle
└── src/
    ├── main/kotlin/
    ├── main/resources/
    ├── test/kotlin/
    └── test/resources/

Подробнее: Структура проекта

Что делает компилятор K2

K2 - фронтенд компилятора, стабильный с Kotlin 2.0 и единственный в 2.x. Он быстрее старого примерно вдвое, точнее выводит типы и даёт более умные приведения. Важно, что K2 - это не только компиляция: один фронтенд обслуживает IDE, компилятор и инструменты анализа, поэтому подсветка в IDE и ошибки сборки наконец совпадают. Подробнее: Компилятор K2

Версии: три разных числа

Что Где задаётся Что значит
версия Kotlin kotlin("jvm") version "2.4.0" версия компилятора и stdlib
languageVersion compilerOptions какие возможности языка разрешены
jvmTarget / toolchain jvmToolchain(21) версия байт-кода и JDK
// build.gradle.kts
plugins { kotlin("jvm") version "2.4.0" }

kotlin {
    jvmToolchain(21)                    // JDK для компиляции и запуска

    compilerOptions {
        // можно ограничить язык более старой версией
        // languageVersion.set(KotlinVersion.KOTLIN_2_1)
        allWarningsAsErrors.set(false)
    }
}

Частая ошибка - считать их одним. Можно компилировать Kotlin 2.4 компилятором, ограничив язык версией 2.1 (для совместимости с библиотекой), и выдавать байт-код для JVM 8.

Качество кода

ktlint - форматирование по официальному style guide, почти без настроек. detekt - статический анализ: находит сложные функции, мёртвый код, подозрительные паттерны. Они дополняют друг друга: первый про форму, второй про смысл. Подробнее: Линтеры и формат

Карта раздела

Заметка О чём
kotlinc CLI, скрипты, REPL, ключевые флаги
Gradle и Kotlin DSL конфигурации, version catalog, таски
Структура проекта каталоги, модули, смешанные исходники
Компилятор K2 фазы, что изменилось, флаги
KSP кодогенерация, процессоры, миграция с kapt
Линтеры и формат ktlint, detekt, CI

Что ещё есть в экосистеме

Инструмент Задача
Dokka документация из KDoc (аналог Javadoc)
binary-compatibility-validator контроль публичного ABI библиотеки
kotlinx-benchmark микробенчмарки на JMH
Kover покрытие тестами
kotlin-multiplatform общий код для JVM/JS/Native
Compose Multiplatform UI поверх Multiplatform
kotlinx-serialization сериализация через плагин компилятора
Amper экспериментальная альтернатива Gradle от JetBrains
// build.gradle.kts с типичным набором для библиотеки
plugins {
    kotlin("jvm") version "2.4.0"
    kotlin("plugin.serialization") version "2.4.0"
    id("org.jetbrains.dokka") version "2.0.0"
    id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.17.0"
    id("io.gitlab.arturbosch.detekt") version "1.23.8"
}

repositories { mavenCentral() }

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
    testImplementation(kotlin("test"))
}

Обратите внимание: kotlinx-serialization - это плагин компилятора, а не библиотека рефлексии. Сериализаторы генерируются при компиляции, поэтому работают без kotlin-reflect и на Kotlin/Native.

[!info] Отличия от Java

Java Kotlin
javac kotlinc (K2)
Maven или Gradle (Groovy) Gradle + Kotlin DSL
annotation processing (APT) KSP
Checkstyle, SpotBugs, PMD ktlint, detekt
Javadoc Dokka (KDoc)
Lombok возможности языка (data class, свойства)
один таргет - JVM JVM, JS, Native, Wasm

Gradle в Kotlin-проектах не альтернатива, а стандарт. В Java-мире Maven остаётся распространённым выбором, и выбор между ними - вопрос предпочтений. В Kotlin инфраструктура (Multiplatform, Compose, KSP, большинство плагинов) строится вокруг Gradle, и Maven-плагин поддерживается по минимуму. Это сужает выбор, но концентрирует усилия.

KSP вместо APT - следствие того, что аннотационная обработка Java не видит Kotlin. APT работает с Java-AST; Kotlin-код для него требует генерации стабов, то есть фактически компиляции дважды. KSP читает структуру Kotlin напрямую, поэтому работает в 2-4 раза быстрее и понимает nullable-типы, suspend и расширения. Цена - процессоры нужно писать заново: API другой.

Lombok не нужен. Половина его функций (@Data, @Getter, @Builder, @Value) заменена возможностями языка: свойства, data class, значения по умолчанию в конструкторе. Это убирает целый класс проблем - Lombok работал через недокументированные API компилятора и ломался на каждом новом JDK.

Одна система сборки обслуживает несколько платформ. Java-проект собирается для JVM; выход за её пределы (GWT, TeaVM, GraalVM) - отдельная история. В Kotlin JS, Native и Wasm - полноправные таргеты в том же build.gradle.kts, с общим исходным кодом. Цена - сложность конфигурации Multiplatform-проекта заметно выше.

Сравнить: JVM

[!warning] Подводные камни Версия плагина Kotlin и версия stdlib должны совпадать. Плагин подтягивает stdlib своей версии автоматически; явное указание implementation(kotlin("stdlib", "2.3.0")) при плагине 2.4.0 даёт конфликт и предупреждение. Не указывайте stdlib вручную вообще.

jvmTarget без toolchain ломается на другом JDK. Если локально JDK 21, а в CI - 17, сборка с jvmTarget = "21" упадёт. jvmToolchain(21) решает: Gradle сам скачает нужный JDK.

jvmTarget Kotlin и targetCompatibility Java должны совпадать. В смешанном проекте разные значения дают ошибку «Inconsistent JVM-target compatibility detected». jvmToolchain настраивает оба.

Значение по умолчанию jvmTarget - 1.8. Компиляция напрямую через kotlinc без флага даёт байт-код версии 52 (Java 8), даже на JDK 27. Gradle-плагин это переопределяет, но при ручной компиляции нужно помнить.

kapt и KSP в одном проекте замедляют сборку. Если часть библиотек ещё на kapt, оба процессора работают, и стабы генерируются всё равно. Миграцию стоит делать целиком.

Gradle без gradlew - источник расхождений. Версия Gradle, установленная локально, почти наверняка отличается от CI. Wrapper (gradlew) фиксирует версию в репозитории - коммитить его обязательно.

kotlin.incremental может давать ложные сборки. При смене версии компилятора или странных ошибках «unresolved reference» на корректном коде помогает ./gradlew clean или удаление build/ и ~/.gradle/caches/build-cache-1.

Плагины компилятора фиксируют версию Kotlin. kotlin("plugin.serialization") version "2.4.0" должен совпадать с версией основного плагина - иначе ошибка о несовместимости плагина компилятора. Используйте version catalog, чтобы версия была в одном месте.

Далее: kotlinc