遭遇幽灵:什么是"unrecognized Attribute name MODULE"?
\n你是否见过一种充满神秘感的错误?想象一下:你正在用现代Kotlin编写代码,一切运行流畅,Gradle正常运转,突然——砰!——日志中出现了类似这样的内容:
\nunrecognized Attribute name MODULE (class com.sun.tools.javac.util.SharedNameTable$NameImpl)\n你去谷歌搜索,发现Stack Overflow上有成千上万的浏览量,然后意识到:这不仅仅是一个bug。这是——兼容性错误——发生在不同世界之间。在Java和Kotlin之间。在旧的构建系统和新的模块化世界之间。
\n在这篇文章中,我们不仅会提供解决方案。我们还将深入Java编译器的内部,解析什么是SharedNameTable,为什么JVM会对MODULE这个词报错,以及这与你的Kotlin项目有何关联。剧透:罪魁祸首不是Kotlin,而是旧的Gradle和Java 9+。
简要说明:如何修复(给急性子的人)
\n如果你需要紧急修复构建——这里有三种可行方法。根据情况选择:
\n- \n
- 方法1(推荐):将Gradle升级到7.0+版本,并将Kotlin Gradle Plugin升级到1.5+。这将永久解决问题。 \n
- 方法2(快速解决方案):在
build.gradle.kts中显式指定JVM target: \n
kotlin {\n jvmToolchain(11) // 或者8,如果你想要旧版兼容性\n}\n- \n
- 方法3(临时方案):为编译器添加标志: \n
tasks.withType<KotlinCompile> {\n kotlinOptions {\n jvmTarget = "1.8"\n }\n}\n但如果你想理解为什么这样有效——请继续阅读。我们将深入Java编译器的深处。
\n\n幕后揭秘:什么是com.sun.tools.javac.util.SharedNameTable$NameImpl?
\n让我们逐部分解析这个可怕的字符串。这不仅仅是一串随机的单词——它指向Java Development Kit (JDK)内的一个特定类。
\n- \n
com.sun.tools.javac——内部Java编译器(javac)的包。没错,就是那个将.java转换成.class的编译器。 \n util.SharedNameTable——一个工具类,用于在字符串池中存储名称(变量、方法、类),以进行优化。这是一个缓存,避免创建数百万个相同的字符串。 \n $NameImpl——一个内部类,表示具体的名称。当编译器遇到module这个词时,它会创建一个NameImpl对象。 \n
现在最有趣的部分来了:当javac在class文件中遇到MODULE属性,但不知道如何处理时,就会发生错误。如果你使用的是旧编译器(< Java 9),或者反过来,使用新编译器但旧的class文件格式,就会发生这种情况。
\n\n为什么MODULE是一个特殊词?
\n从Java 9开始,语言中引入了模块系统(Project Jigsaw)。关键字module成为保留字。在class文件中出现了新的属性,如Module、ModulePackages、ModuleMainClass等。
当Kotlin编译器生成字节码时,如果你的JVM target设置为9+,它可能会偶然(或有意)添加这个属性。而旧的Gradle或Maven,使用过时的javac版本,无法理解这些属性。错误由此产生。
\n\n实际示例:Kotlin + Gradle + Java 11
\n想象一个基于Gradle的简单Kotlin项目。以下是build.gradle.kts文件:
plugins {\n kotlin("jvm") version