¿Por qué su proyecto en Kotlin y Java se «desmorona» de repente?
\n\n¿Alguna vez se ha encontrado en una situación en la que escribe código en Kotlin, usa Java 11 para el código principal, y Gradle de repente muestra un error de compatibilidad? ¿Le suena familiar? Este es uno de los problemas más comunes al configurar proyectos multimódulo. Especialmente cuando usa build.gradle.kts (Kotlin DSL) e intenta armonizar compileJava con compileKotlin. En este artículo, analizaremos cómo establecer correctamente la compatibilidad de destino JVM para Java 11 y Kotlin 1.8, para que su proyecto se compile sin errores. Y sí, lo haremos en español, con humor y sin teoría aburrida.
Si vino de Stack Overflow, sabrá que la pregunta original ha acumulado miles de visitas. Lo adaptaremos a un formato educativo para que no solo copie la respuesta, sino que entienda por qué ocurre el error y cómo evitarlo en el futuro.
\n\nEl problema: cuando Java y Kotlin hablan diferentes idiomas
\n\nImagine que Java 11 es un profesor estricto que exige que todo esté según el estándar. Y Kotlin 1.8 es su colega creativo que quiere usar las nuevas funciones, pero se olvida de informar al profesor. El resultado es un conflicto.
\n\nEn Gradle, cada tarea de compilación (compileJava, compileKotlin) tiene su propio destino JVM: la versión del bytecode en la que se compila el código. Si no coinciden, obtendrá un error como:
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\nO, lo que es peor, una caída silenciosa del rendimiento debido a la incompatibilidad del bytecode.
\n\n¿Por qué ocurre esto precisamente en build.gradle.kts?
\n\nKotlin DSL (archivos .gradle.kts) es una herramienta potente, pero requiere la especificación explícita de los parámetros. A diferencia de Groovy DSL, donde algunas configuraciones se «toman» automáticamente, en Kotlin DSL debe ser preciso. Esto se aplica especialmente al destino JVM.
Error típico: establece java.targetCompatibility = JavaVersion.VERSION_11, pero se olvida de especificar lo mismo para Kotlin. O viceversa: configura compileKotlin, pero compileJava se queda en Java 8.
La solución correcta: un destino JVM unificado para Java y Kotlin
\n\nPara evitar errores, debe establecer explícitamente el destino JVM para ambas tareas. Así es como se hace en build.gradle.kts:
Paso 1. Configuración de Java (compileJava)
\n\nAñada en el bloque java {} o en 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\nTenga en cuenta: sourceCompatibility y targetCompatibility deben ser iguales. Si escribe código en Java 11, el bytecode también debe ser Java 11.
Paso 2. Configuración de Kotlin (compileKotlin)
\n\nAhora lo más importante: establecer el destino JVM para Kotlin. Esto se hace a través del bloque kotlin {} o mediante tasks.withType<KotlinCompile>():
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\ntasks.withType<KotlinCompile>() {\n kotlinOptions {\n jvmTarget = "11" // Atención: esto es una cadena, ¡no JavaVersion!\n apiVersion = "1.8"\n languageVersion = "1.8"\n }\n}\n\n\n¿Por qué jvmTarget es una cadena? Porque Kotlin espera una representación de cadena de la versión (por ejemplo, "1.8", "11", "17"). Si intenta pasar JavaVersion.VERSION_11, el compilador mostrará un error.
Ejemplo completo de build.gr