JVM-совместимость Gradle: как подружить Kotlin 1.8 и Java 11

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему ваш проект на Kotlin и Java внезапно «разваливается»?

Вы когда-нибудь сталкивались с ситуацией, когда пишете код на Kotlin, используете Java 11 для основного кода, а Gradle вдруг выдает ошибку совместимости? Знакомо? Это одна из самых частых проблем при настройке многомодульных проектов. Особенно, когда вы используете build.gradle.kts (Kotlin DSL) и пытаетесь подружить compileJava с compileKotlin. В этой статье мы разберем, как правильно задать jvm target compatibility для Java 11 и Kotlin 1.8, чтобы ваш проект собирался без ошибок. И да, мы сделаем это на русском, с юмором и без скучной теории.

Если вы пришли с Stack Overflow, то знаете, что оригинальный вопрос собрал тысячи просмотров. Мы адаптируем его под образовательный формат, чтобы вы не просто скопировали ответ, а поняли, почему возникает ошибка и как её избежать в будущем.

Проблема: когда Java и Kotlin говорят на разных языках

Представьте, что Java 11 — это строгий профессор, который требует, чтобы всё было по стандарту. А Kotlin 1.8 — это его креативный коллега, который хочет использовать новые фишки, но забывает сообщить профессору. В итоге — конфликт.

В Gradle каждая задача компиляции (compileJava, compileKotlin) имеет свой JVM target — версию байт-кода, в которую компилируется код. Если они не совпадают, вы получите ошибку вроде:

Error: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.8.0, expected version is 1.6.0.

Или, что ещё хуже, — молчаливое падение производительности из-за несовместимости байт-кода.

Почему это происходит именно в build.gradle.kts?

Kotlin DSL (файлы .gradle.kts) — это мощный инструмент, но он требует явного указания параметров. В отличие от Groovy DSL, где некоторые настройки «подхватываются» автоматически, в Kotlin DSL нужно быть точным. Особенно это касается jvm target.

Типовая ошибка: вы задаёте java.targetCompatibility = JavaVersion.VERSION_11, но забываете указать то же самое для Kotlin. Или наоборот — настраиваете compileKotlin, а compileJava остаётся на Java 8.

Правильное решение: единый JVM target для Java и Kotlin

Чтобы избежать ошибок, нужно явно задать jvm target для обеих задач. Вот как это сделать в build.gradle.kts:

Шаг 1. Настройка Java (compileJava)

Добавьте в блок java {} или в tasks.withType<JavaCompile>():

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

tasks.withType<JavaCompile>() {
    options.encoding = "UTF-8"
    options.compilerArgs.add("-parameters")
}

Обратите внимание: sourceCompatibility и targetCompatibility должны быть одинаковыми. Если вы пишете код на Java 11, то и байт-код должен быть Java 11.

Шаг 2. Настройка Kotlin (compileKotlin)

Теперь самое важное — задать jvm target для Kotlin. Это делается через блок kotlin {} или через tasks.withType<KotlinCompile>():

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

tasks.withType<KotlinCompile>() {
    kotlinOptions {
        jvmTarget = "11"  // Внимание: это строка, а не JavaVersion!
        apiVersion = "1.8"
        languageVersion = "1.8"
    }
}

Почему jvmTarget — это строка? Потому что Kotlin ожидает именно строковое представление версии (например, "1.8", "11", "17"). Если вы попытаетесь передать JavaVersion.VERSION_11, компилятор выдаст ошибку.

Полный пример build.gradle.kts:

plugins {
    java
    kotlin("jvm") version "1.8.0"
}

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

tasks.withType<JavaCompile>() {
    options.encoding = "UTF-8"
}

tasks.withType<KotlinCompile>() {
    kotlinOptions {
        jvmTarget = "11"
        apiVersion = "1.8"
        languageVersion = "1.8"
    }
}

Топ-5 ошибок при настройке JVM target и как их избежать

Давайте разберём самые популярные грабли, на которые наступают разработчики:

Ошибка 1: Забыли указать jvmTarget для Kotlin

Ваш код на Java компилируется в Java 11, а Kotlin — в Java 8. Результат: ошибки во время выполнения или при использовании Java 11-специфичных фич.

Решение: Всегда явно задавайте jvmTarget в kotlinOptions.

Ошибка 2: Путаница между JavaVersion и строкой

В Java вы используете JavaVersion.VERSION_11, а в Kotlin — "11". Если перепутать, Gradle выдаст ошибку компиляции.

Решение: Запомните: для Java — JavaVersion, для Kotlin — строка.

Ошибка 3: Несоответствие версий Kotlin и Java

Например, вы используете Kotlin 1.8, но указываете jvmTarget = "1.8" (Java 8). Это может работать, но вы теряете возможность использовать фичи Java 11.

Решение: Убедитесь, что jvmTarget совпадает с версией Java, которую вы используете в проекте.

Ошибка 4: Глобальная настройка в allprojects или subprojects

Если вы задаёте jvmTarget глобально, но в одном из модулей используете другую версию, конфликт неизбежен.

Решение: Настраивайте jvmTarget индивидуально для каждого модуля, если версии различаются.

Ошибка 5: Игнорирование apiVersion и languageVersion

Эти параметры не обязательны, но их отсутствие может привести к неожиданным ошибкам при использовании новых API Kotlin.

Решение: Укажите их, чтобы зафиксировать версию Kotlin, с которой работает проект.

Продвинутый уровень: автоматическая синх

Блоги

Рекомендаций по книгам