为什么你的Kotlin和Java项目会突然“崩溃”?
\n\n你是否遇到过这样的情况:用Kotlin编写代码,使用Java 11作为主要代码,而Gradle突然报出兼容性错误?熟悉吗?这是配置多模块项目时最常见的问题之一。尤其是当你使用build.gradle.kts(Kotlin DSL)并试图让compileJava与compileKotlin协同工作时。在本文中,我们将详细讲解如何正确设置JVM目标兼容性,使你的项目能够无错误地构建。没错,我们将用中文、幽默且不枯燥的方式来完成。
如果你是从Stack Overflow过来的,那么你知道原始问题已经获得了数千次浏览。我们将其改编为教育性内容,让你不仅仅是复制答案,而是理解错误产生的原因以及如何在未来避免它。
\n\n问题:当Java和Kotlin说不同的语言时
\n\n想象一下,Java 11是一位严格的教授,要求一切都符合标准。而Kotlin 1.8则是一位富有创造力的同事,想要使用新功能,但忘记通知教授。结果就是——冲突。
\n\n在Gradle中,每个编译任务(compileJava、compileKotlin)都有自己的JVM目标——代码编译成的字节码版本。如果它们不匹配,你会得到类似这样的错误:
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.\n\n\n或者更糟的是——由于字节码不兼容而导致性能无声下降。
\n\n为什么这恰恰发生在build.gradle.kts中?
\n\nKotlin DSL(.gradle.kts文件)是一个强大的工具,但它需要显式指定参数。与Groovy DSL不同,后者某些设置会自动“捕获”,而在Kotlin DSL中,你必须精确。特别是关于JVM目标的设置。
典型错误:你设置了java.targetCompatibility = JavaVersion.VERSION_11,但忘记为Kotlin指定相同的设置。或者反过来——配置了compileKotlin,而compileJava仍然停留在Java 8上。
正确的解决方案:为Java和Kotlin设置统一的JVM目标
\n\n为了避免错误,需要为两个任务显式设置JVM目标。以下是在build.gradle.kts中的操作方法:
步骤1. 配置Java(compileJava)
\n\n在java {}块或tasks.withType<JavaCompile>()中添加:
java {\n sourceCompatibility = JavaVersion.VERSION_11\n targetCompatibility = JavaVersion.VERSION_11\n}\n\ntasks.withType<JavaCompile>() {\n options.encoding = "UTF-8"\n options.compilerArgs.add("-parameters")\n}\n\n\n请注意:sourceCompatibility和targetCompatibility必须相同。如果你用Java 11编写代码,那么字节码也应该是Java 11。
步骤2. 配置Kotlin(compileKotlin)
\n\n现在最重要的是——为Kotlin设置JVM目标。这可以通过kotlin {}块或tasks.withType<KotlinCompile>()来完成:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\ntasks.withType<KotlinCompile>() {\n kotlinOptions {\n jvmTarget = "11" // 注意:这是字符串,而不是JavaVersion!\n apiVersion = "1.8"\n languageVersion = "1.8"\n }\n}\n\n\n为什么jvmTarget是字符串?因为Kotlin期望的是版本的字符串表示形式(例如,"1.8"、"11"、"17")。如果你尝试传递JavaVersion.VERSION_11,编译器会报错。
完整的build.gr