Unrecognized Attribute Name MODULE в Kotlin: разбор ошибки

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

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

Начать курс

Встреча с призраком: что такое "unrecognized Attribute name MODULE"?

Вы когда-нибудь видели ошибку, от которой веет мистикой? Представьте: вы пишете на современном Kotlin, всё летает, Gradle жужжит, и вдруг — бам! — в логах появляется что-то вроде:

unrecognized Attribute name MODULE (class com.sun.tools.javac.util.SharedNameTable$NameImpl)

Вы гуглите, находите Stack Overflow с тысячами просмотров, и понимаете: это не просто баг. Это — ошибка совместимости между мирами. Между Java и Kotlin. Между старой системой сборки и новым модульным миром.

В этой статье мы не просто дадим решение. Мы заглянем под капот Java-компилятора, разберём, что такое SharedNameTable, почему JVM ругается на слово MODULE, и как это связано с вашим Kotlin-проектом. Спойлер: виноват не Kotlin, а старый Gradle и Java 9+.

Коротко: как исправить (для нетерпеливых)

Если вам нужно срочно починить сборку — вот три рабочих способа. Выбирайте по ситуации:

  • Способ 1 (рекомендуемый): Обновите Gradle до версии 7.0+ и Kotlin Gradle Plugin до 1.5+. Это решит проблему навсегда.
  • Способ 2 (быстрое решение): Явно укажите JVM target в build.gradle.kts:
kotlin {
    jvmToolchain(11) // или 8, если хотите старую совместимость
}
  • Способ 3 (костыль): Добавьте флаг компилятору:
tasks.withType<KotlinCompile> {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

Но если вы хотите понять, почему это работает — читайте дальше. Мы нырнём в глубины Java-компилятора.

Под капотом: что такое com.sun.tools.javac.util.SharedNameTable$NameImpl?

Давайте разберём эту страшную строку по частям. Это не просто случайный набор слов — это указание на конкретный класс внутри Java Development Kit (JDK).

  • com.sun.tools.javac — пакет внутреннего Java-компилятора (javac). Да, того самого, который превращает .java в .class.
  • util.SharedNameTable — служебный класс, который хранит имена (переменных, методов, классов) в пуле строк для оптимизации. Это кэш, чтобы не создавать миллион одинаковых строк.
  • $NameImpl — внутренний класс, представляющий конкретное имя. Когда компилятор встречает слово module, он создаёт объект NameImpl.

А теперь самое интересное: ошибка возникает, когда javac натыкается на атрибут MODULE в class-файле, но не знает, что с ним делать. Это происходит, если вы используете старый компилятор (< Java 9) или, наоборот, новый компилятор, но старый формат class-файлов.

Почему MODULE — особое слово?

Начиная с Java 9, в языке появилась система модулей (Project Jigsaw). Ключевое слово module стало зарезервированным. В class-файлах появился новый атрибут Module, ModulePackages, ModuleMainClass и т.д.

Когда Kotlin-компилятор генерирует байт-код, он может случайно (или намеренно) добавить этот атрибут, если ваш JVM target установлен в 9+. А старый Gradle или Maven, используя устаревшую версию javac, не понимает этих атрибутов. Отсюда и ошибка.

Реальный пример: Kotlin + Gradle + Java 11

Представьте простой Kotlin-проект на Gradle. Вот файл build.gradle.kts:

plugins {
    kotlin("jvm") version "1.4.32"
    application
}

dependencies {
    implementation(kotlin("stdlib"))
}

application {
    mainClass.set("MainKt")
}

И простой файл Main.kt:

fun main() {
    println("Hello, World!")
}

Если вы запустите сборку с JDK 11, но с Gradle 6.8 — вы с вероятностью 99% получите ту самую ошибку. Почему? Потому что Kotlin 1.4.32 по умолчанию генерирует байт-код для JVM 1.8, но Gradle 6.8 использует внутренний javac, который путается в атрибутах.

Решение — обновить Kotlin Gradle Plugin до 1.5+ и Gradle до 7.0+. Или явно указать jvmTarget = "1.8".

Глубже: как Kotlin взаимодействует с Java-компилятором?

Многие думают, что Kotlin — это отдельный язык со своим компилятором. Так и есть: kotlinc компилирует .kt файлы в байт-код. Но есть нюанс: Kotlin-компилятор использует Java-компилятор (javac) для генерации некоторых метаданных, особенно при работе с Java-библиотеками и аннотациями.

Когда вы пишете:

@JvmStatic
fun myFunction() { ... }

Kotlin генерирует class-файл, который содержит атрибуты, понятные JVM. Если ваша JVM (или инструмент, который читает class-файлы) слишком старая, она может не распознать новый атрибут. Ошибка unrecognized Attribute name MODULE — это именно такой случай.

Анатомия class-файла: что говорит спецификация

Каждый class-файл начинается с магического числа 0xCAFEBABE, затем идёт версия, пул констант, флаги доступа, и — самое важное — атрибуты. Атрибуты

Блоги

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