Kotlin 2.2 en 2026: K2 compiler y las novedades que cambian tu código

Kotlin 2.2 salió el 23 de junio de 2025 y, aunque en apariencia es una versión de consolidación, trae cambios que merece la pena conocer bien. El compilador K2, que se estabilizó en Kotlin 2.0, sigue ganando peso: más features del lenguaje dependen de él, el IDE lo usa por defecto y algunos patrones de código que antes compilaban sin queja ahora fallan. Esta guía repasa lo más importante: qué cambió con K2, qué hay de nuevo en 2.2 y cómo actualizar sin sorpresas.

El compilador K2: qué es y por qué importa

El compilador K2 es una reescritura completa del frontend de Kotlin: la parte que analiza el código, resuelve las llamadas y hace la inferencia de tipos. No cambia el backend ni el bytecode que genera, así que tu código compilado con K2 funciona exactamente igual que antes. Lo que cambia es la velocidad y la precisión del análisis.

Antes de Kotlin 2.0 el frontend acumulaba deuda técnica desde los primeros años del lenguaje. El análisis de tipos era secuencial y lento, y añadir features nuevas requería ingeniería complicada alrededor de estructuras de datos que no estaban pensadas para crecer tanto. K2 arranca de cero con una representación unificada de toda la información semántica, lo que permite análisis más rápidos y smart casts más inteligentes.

Los benchmarks del equipo de JetBrains sobre proyectos reales (Anki-Android y Exposed) dan estas cifras:

  • Build limpio: de 57,7 s con Kotlin 1.9 a 29,7 s con Kotlin 2.0. Casi un 50% menos.
  • Fase de inicialización en builds incrementales: hasta un 488% más rápida.
  • Fase de análisis en builds incrementales: hasta un 376% más rápida.

En proyectos grandes con muchos módulos la diferencia es apreciable desde el primer día. En proyectos pequeños el build ya era rápido, así que el salto se nota menos, pero el IDE responde mejor en cualquier caso.

K2 en el IDE: IntelliJ y Android Studio

Desde IntelliJ IDEA 2024.1 y las versiones recientes de Android Studio, el K2 mode está activo por defecto para el análisis de código. El IDE usa el mismo frontend que el compilador para hacer highlighting, completado y detección de errores, lo que elimina la vieja divergencia entre «compila pero el IDE marca error» o al revés.

No hay nada que configurar. Si usas una versión reciente del IDE y Kotlin 2.x en tu proyecto, ya tienes K2 funcionando.

Smart casts mejorados con K2

Una de las mejoras más prácticas de K2 es que hace smart casts en situaciones donde el compilador anterior se rendía. Algunos ejemplos concretos:

Variables locales comprobadas fuera del if

class Cat { fun purr() { println("Purr purr") } }

fun petAnimal(animal: Any) {
    val isCat = animal is Cat
    if (isCat) {
        animal.purr() // K2: funciona. K1: error de compilación.
    }
}

OR lógico entre type checks

interface Status { fun signal() {} }
interface Ok : Status
interface Postponed : Status
interface Declined : Status

fun signalCheck(signalStatus: Any) {
    if (signalStatus is Postponed || signalStatus is Declined) {
        signalStatus.signal() // K2: smart cast al supertipo común Status.
    }
}

Propiedades con tipo función

class Holder(val provider: (() -> Unit)?) {
    fun process() {
        if (provider != null) {
            provider() // K2: funciona. K1: requería provider?.invoke().
        }
    }
}

Lambdas de funciones inline

K2 entiende que las funciones inline ejecutan la lambda en el mismo hilo y sin guardar referencias, así que puede hacer smart casts dentro de esas lambdas aunque la variable sea mutable:

inline fun inlineAction(f: () -> Unit) = f()

fun runProcessor(): Processor? {
    var processor: Processor? = null
    inlineAction {
        if (processor != null) {
            processor.process() // K2: OK. K1: requería safe call.
        }
    }
    return processor
}

Guards en when: ya estables en Kotlin 2.2

Los guards en when llegaron en preview con Kotlin 2.1 y en 2.2 son estables. La idea es añadir una condición adicional dentro del branch de un patrón:

fun describe(animal: Any): String = when (animal) {
    is Cat if animal.indoor -> "gato de interior"
    is Cat -> "gato"
    is Dog -> "perro"
    else -> "otro"
}

La guarda (if condition) se evalúa solo si el patrón encaja. Antes de esto tenías que anidar un if dentro del branch o combinar condiciones compuestas con && fuera del patrón, que era más verboso y menos legible.

También llegan como estables en 2.2 el break y continue no locales y la interpolación de strings con múltiples signos de dólar ($$), útil cuando el string contiene literales con $ sin que Kotlin los interprete como interpolación.

Context parameters: la gran preview de 2.2

La novedad más llamativa de Kotlin 2.2 en modo preview son los context parameters. Permiten que una función declare dependencias implícitas que tienen que estar disponibles en el ámbito del llamador:

interface UserService {
    fun log(message: String)
    fun findUserById(id: Int): String
}

context(users: UserService)
fun outputMessage(message: String) {
    users.log("Log: $message")
}

context(users: UserService)
val firstUser: String
    get() = users.findUserById(1)

Para activarlos en tu proyecto:

// build.gradle.kts
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xcontext-parameters")
    }
}

Son una evolución de los context receivers experimentales de versiones anteriores, con una sintaxis más clara y mejor integración con el sistema de tipos. De momento son preview, así que no los uses en código de producción sin asumir que la API puede cambiar antes de la versión estable.

Context-sensitive resolution: menos ruido en when

Otra preview de 2.2: cuando el tipo esperado se conoce por contexto, puedes omitir el nombre del tipo al referenciar sus miembros.

// Antes
fun message(problem: Problem): String = when (problem) {
    Problem.CONNECTION -> "connection"
    Problem.AUTHENTICATION -> "authentication"
    Problem.DATABASE -> "database"
    Problem.UNKNOWN -> "unknown"
}

// Con -Xcontext-sensitive-resolution
fun message(problem: Problem): String = when (problem) {
    CONNECTION -> "connection"
    AUTHENTICATION -> "authentication"
    DATABASE -> "database"
    UNKNOWN -> "unknown"
}

El compilador infiere que CONNECTION es Problem.CONNECTION porque el argumento de when es de tipo Problem. Menos ruido en enums grandes.

KSP2: el procesador de símbolos para K2

Si tu proyecto usa kapt para procesamiento de anotaciones (Room, Hilt, Moshi...), tienes trabajo pendiente. kapt funciona con K2 pero sigue siendo considerablemente más lento que KSP porque necesita generar stubs Java antes de procesar. KSP2 usa directamente las APIs del frontend K2 y es mucho más rápido.

Los procesadores más usados ya tienen soporte KSP: Room, Hilt, Moshi KSP y otros. Para migrar, basta con cambiar el plugin y la dependencia:

// build.gradle.kts
plugins {
    id("com.google.devtools.ksp") version "2.2.0-1.0.x"
}

dependencies {
    // En vez de kapt("androidx.room:room-compiler:x.x.x")
    ksp("androidx.room:room-compiler:x.x.x")
}

Para proyectos nuevos, usa KSP desde el principio siempre que el procesador lo soporte. En proyectos existentes, migra módulo a módulo: no es necesario hacerlo todo a la vez.

Kotlin Multiplatform y K2

Con K2, la compilación hacia distintos targets (JVM, Native, JS, Wasm) es más rápida porque el frontend se ejecuta una sola vez y produce la representación unificada que cada backend consume. Antes el análisis se repetía por target.

K2 también corrige un problema real con KMP: el código común ya no puede resolver símbolos de plataforma durante el análisis. Esto puede romper código que funcionaba antes por accidente:

// commonMain
fun foo(x: Any) = println("common foo")
fun exampleFunction() { foo(42) }

// jvmMain
fun foo(x: Int) = println("platform foo")

Con K1, en JVM se llamaba a foo(Int) y en JS a foo(Any). Con K2, ambas plataformas resuelven a foo(Any) del código común. El comportamiento es consistente pero puede ser distinto al que tenías. Revisa los tests multiplataforma al migrar.

Kotlin/JVM: -jvm-default ya es estable

En 2.2 la opción -jvm-default pasa a ser estable y reemplaza a la experimental -Xjvm-default. Controla cómo se generan los métodos por defecto en interfaces:

// build.gradle.kts
kotlin {
    compilerOptions {
        jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
    }
}

El modo NO_COMPATIBILITY genera solo las implementaciones por defecto sin los bridges de compatibilidad, lo que reduce el tamaño del bytecode. Úsalo en proyectos que no necesiten compatibilidad binaria con consumidores compilados con versiones viejas del compilador.

Gradle: requisitos de versión y novedades

Kotlin 2.2 requiere Gradle 7.6.3 como mínimo y soporta hasta Gradle 8.14. Para actualizar el plugin:

// build.gradle.kts
plugins {
    kotlin("jvm") version "2.2.0"
    // o para Android:
    kotlin("android") version "2.2.0"
}

kotlin {
    jvmToolchain(21) // configura el JDK que usa el compilador
}

La opción kotlinOptions {} queda deprecated a nivel de error en 2.2. Si la usas, tendrás que migrar a compilerOptions {}:

// Antes (deprecated)
tasks.withType().configureEach {
    kotlinOptions {
        jvmTarget = "21"
    }
}

// Ahora
kotlin {
    compilerOptions {
        jvmTarget.set(JvmTarget.JVM_21)
    }
}

Otro cambio a tener en cuenta: KotlinCompileTool.setSource() ahora reemplaza las fuentes en vez de añadir. Si la usabas para agregar fuentes adicionales, cámbiala por source().

Cómo migrar un proyecto a Kotlin 2.2

La migración es más sencilla de lo que parece. Estos son los pasos habituales:

  • Actualiza kotlin-gradle-plugin a 2.2.0 en build.gradle.kts.
  • Verifica que Gradle es 7.6.3 o superior.
  • Sustituye kotlinOptions {} por compilerOptions {} donde lo uses.
  • Reemplaza kapt por ksp en los módulos donde el procesador lo soporte.
  • Ejecuta ./gradlew build --info y revisa los warnings de migración.
  • Si usas KMP, ejecuta los tests en todos los targets: la corrección del comportamiento con código de plataforma puede cambiar resultados.

La mayoría de proyectos compilan sin tocar el código de la aplicación. Los cambios más frecuentes son de configuración de Gradle, no de lógica.

Si quieres ver cuánto gana tu build con K2, activa los build reports de Kotlin en gradle.properties:

kotlin.build.report.output=file

El informe aparece en build/reports/kotlin-build/ con los tiempos desglosados por fase.

Qué viene a continuación

Los context parameters de 2.2 son la feature que más va a cambiar la forma de escribir código Kotlin en los próximos años. Permiten un estilo funcional con dependencias implícitas que es más limpio que pasar objetos de contexto como parámetros explícitos o usar inyección de dependencias para casos simples.

Si quieres entender mejor el lenguaje desde sus bases, te recomiendo leer sobre Kotlin: el lenguaje moderno que desbancó a Java en Android y también por qué Kotlin es el lenguaje preferido para Android.

Imagen: Pexels / ThisIsEngineering

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP