K2 adalah kompilator frontend baru untuk Kotlin, diperkenalkan sebagai default mulai Kotlin 2.0. Sebelumnya, kompilator lama (disebut K1) memakai arsitektur dua-fase berbasis FIR dan Psi yang sudah berusia satu dekade. K2 menyatukan kedua fase menjadi satu FIR (Frontend Intermediate Representation) yang lebih efisien, menghasilkan kompilasi 1.5–2 kali lebih cepat dan inferensi tipe yang lebih akurat. Pada chapter ini kita akan melihat perbedaan praktis yang dirasakan penulis Kotlin sehari-hari, cara mengaktifkan K2, dan kapan harus mundur ke K1 bila menemui bug.
Apa yang Berubah di K2 #
K2 bukan hanya soal kecepatan. Beberapa perilaku kompilator juga berubah:
- Smart cast lebih luas — K2 bisa melakukan smart cast lewat
&&dan||yang sebelumnya membutuhkan!!. - Inferensi lambda parameter — K2 menyimpulkan tipe parameter lambda dari konteks dengan lebih akurat.
- Pesan kesalahan lebih jelas — K2 memberi pesan kesalahan yang lebih akurat tentang lokasi dan penyebab.
- Builder inference stabil — K2 menganggap builder inference sebagai default, bukan fitur eksperimental.
Cuplikan berikut menunjukkan beberapa kode yang diperbaiki inferensinya oleh K2:
// kotlin_k2_compiler.kt
// Mendemonstrasikan dampak K2 compiler terhadap kode yang kita tulis:
// - inferensi tipe yang lebih akurat
// - smart cast lebih luas
// - destrukturisasi otomatis untuk lambda
// - performa kompilasi lebih cepat
//
// Catatan: K2 adalah kompilator internal; kita tidak bisa "menjalankan" K2
// lewat file .kt ini. Yang bisa kita lakukan adalah menunjukkan kode-kode
// yang diperbaiki inferensinya oleh K2.
// 1. Smart cast yang lebih luas di K2 — bahkan lewat or / safe call
data class Pengguna(val nama: String?, val alamat: String?)
fun sapa(u: Pengguna): String {
return if (u.nama != null && u.alamat != null) {
// K1 sering memerlukan `u.nama!!`; K2 smart-cast otomatis
"Halo ${u.nama} dari ${u.alamat}"
} else {
"Data tidak lengkap"
}
}
// 2. Inferensi tipe lambda parameter — K2 menangkap tipe dari konteks
fun pakaiList(block: (List<String>) -> Unit) {
block(listOf("alpha", "beta", "gamma"))
}
// 3. Destructuring di lambda parameter (data class with componentN)
data class Titik(val x: Int, val y: Int)
fun prosesTitik(block: (Titik) -> String) = block(Titik(3, 4))
// 4. Inferensi return type yang lebih akurat
fun <T> identitas(x: T): T = x
fun ambilString(): String = identitas("halo") // T disimpulkan = String
// 5. Builder inference tidak butuh -Xskip-prerelease-check
fun buatMap(): Map<String, Int> = buildMap {
put("satu", 1)
put("dua", 2)
}
fun main() {
println("=== K2 Compiler (Kotlin 2.0+) ===")
println()
println("--- 1. Smart cast lebih luas ---")
println(sapa(Pengguna("Budi", "Jakarta")))
println(sapa(Pengguna("Siti", null)))
println(sapa(Pengguna(null, "Bandung")))
println()
println("--- 2. Inferensi lambda parameter ---")
pakaiList { daftar ->
println("Daftar diterima: $daftar")
println("Ukuran: ${daftar.size}")
}
println()
println("--- 3. Destructuring di lambda ---")
prosesTitik { (x, y) ->
val jarak = x * x + y * y
"Titik($x,$y) kuadrat-jarak=$jarak"
}.let { println(it) }
println()
println("--- 4. Return type inferensi akurat ---")
val s: String = ambilString()
println("Hasil: $s (panjang=${s.length})")
println()
println("--- 5. Builder inference stabil ---")
val peta = buatMap()
peta.forEach { (k, v) -> println(" $k -> $v") }
println()
println("--- 6. Perbandingan K1 vs K2 (ringkasan) ---")
val perbandingan = listOf(
Triple("Frontend", "2-phase Fir/Psi", "FIR unifikasi"),
Triple("Smart cast", "terbatas pada if-lokal", "menjangkau && dan or"),
Triple("Inferensi lambda", "sering perlu eksplisit", "otomatis"),
Triple("Kecepatan", "baseline", "1.5-2x lebih cepat"),
Triple("Compile error", "kadang membingungkan", "lebih akurat")
)
perbandingan.forEach { (aspek, k1, k2) ->
println(" $aspek")
println(" K1: $k1")
println(" K2: $k2")
}
println()
println("--- 7. Cara mengaktifkan K2 ---")
println("Kotlin 2.0+ memakai K2 sebagai default compiler.")
println("Tidak perlu flag tambahan — Gradle/Maven otomatis memakai K2.")
println("Untuk menonaktifkan (kasus lama): -Xuse-old-backend (deprecated)")
}Output yang Dihasilkan:
=== K2 Compiler (Kotlin 2.0+) ===
--- 1. Smart cast lebih luas ---
Halo Budi dari Jakarta
Data tidak lengkap
Data tidak lengkap
--- 2. Inferensi lambda parameter ---
Daftar diterima: [alpha, beta, gamma]
Ukuran: 3
--- 3. Destructuring di lambda ---
Titik(3,4) kuadrat-jarak=25
--- 4. Return type inferensi akurat ---
Hasil: halo (panjang=4)
--- 5. Builder inference stabil ---
satu -> 1
dua -> 2
--- 6. Perbandingan K1 vs K2 (ringkasan) ---
Frontend
K1: 2-phase Fir/Psi
K2: FIR unifikasi
Smart cast
K1: terbatas pada if-lokal
K2: menjangkau && dan or
Inferensi lambda
K1: sering perlu eksplisit
K2: otomatis
Kecepatan
K1: baseline
K2: 1.5-2x lebih cepat
Compile error
K1: kadang membingungkan
K2: lebih akurat
--- 7. Cara mengaktifkan K2 ---
Kotlin 2.0+ memakai K2 sebagai default compiler.
Tidak perlu flag tambahan — Gradle/Maven otomatis memakai K2.
Untuk menonaktifkan (kasus lama): -Xuse-old-backend (deprecated)Pada cuplikan di atas, smart cast u.nama dan u.alamat bekerja otomatis setelah pemeriksaan != null di kondisi if. Di K1, sering kali kita harus menulis u.nama!! atau memisahkan variabel lokal karena kompilator tidak melakukan smart cast lintas &&. K2 memperbaiki kasus ini.
Arsitektur K2 #
K2 mengganti arsitektur frontend lama yang memakai dua fase (Fir + Psi) dengan satu intermediate representation yang disebut FIR. Perubahan ini memengaruhi cara kompilator menganalisis kode sumber:
| Aspek | K1 | K2 |
|---|---|---|
| Frontend | 2-phase Fir/Psi | FIR unifikasi |
| Smart cast | terbatas pada if-lokal | menjangkau && dan or |
| Inferensi lambda | sering perlu eksplisit | otomatis |
| Kecepatan kompilasi | baseline | 1.5-2x lebih cepat |
| Pesan kesalahan | kadang membingungkan | lebih akurat |
K2 tidak mengubah backend (codegen JVM, JS, Native) — itu tetap memakai infrastruktur lama. K2 hanya mengganti cara kompilator memahami kode sumber. Bagi penulis Kotlin, ini berarti kode yang sama menghasilkan bytecode yang sama (kecuali bug fix yang sengaja dilakukan K2).
Cara Mengaktifkan K2 #
Sejak Kotlin 2.0, K2 adalah default compiler. Kita tidak perlu menambahkan flag apa pun — Gradle dan Maven otomatis memakai K2:
plugins {
kotlin("jvm") version "2.0.21"
}
// Tidak perlu flag tambahan — K2 aktif default
kotlin {
compilerOptions {
// Untuk menonaktifkan K2 (kasus lama):
// freeCompilerArgs.add("-Xuse-old-backend") // deprecated
}
}Bila kita memakai Kotlin 1.9.x dan ingin mencoba K2, tambahkan flag -Xuse-k2. Mulai Kotlin 2.0, flag ini tidak diperlukan karena K2 sudah default.
Dampak pada Library #
K2 mengubah cara kompilator menganalisis metadata Kotlin di library. Hal ini berdampak pada:
- Binary compatibility tetap terjaga — library yang dikompilasi dengan K1 tetap bisa dipakai oleh kode K2 dan sebaliknya.
- Source compatibility meningkat — kode sumber yang sebelumnya memerlukan
!!atau anotasi tipe eksplisit mungkin tidak lagi membutuhkannya di K2. - Behavior compatibility — hasil runtime tidak berubah (kecuali bug fix eksplisit yang didokumentasikan di CHANGELOG).
Kapan Mundur ke K1 #
K2 relatif stabil di 2.0.21, tetapi ada beberapa kasus yang masih membutuhkan K1:
- Bug inferensi tipe di K2 — beberapa kasus edge case inferensi yang salah bisa diatasi dengan K1 sementara menunggu fix.
- Plugin kompilator lama — beberapa plugin lama (mis. kapt lama, plugin eksperimental) belum kompatibel dengan K2.
- Inferensi tipe yang lebih longgar di K1 — bila kodebergantung pada perilaku inferensi K1 yang lebih longgar, K2 mungkin menolak kode tersebut.
Untuk mundur ke K1, tambahkan flag -Xuse-old-backend di freeCompilerArgs. Flag ini sudah deprecated dan akan dihapus di Kotlin versi mendatang, jadi anggap sebagai solusi sementara.
Referensi Lanjutan #
- What’s new in Kotlin 2.0 — ringkasan Kotlin 2.0 yang memperkenalkan arsitektur frontend compiler K2 yang lebih cepat
- Kotlin documentation home — halaman utama dokumentasi Kotlin untuk menelusuri opsi compiler K2 dan migrasi
Selanjutnya #
Setelah memahami K2 sebagai kompilator modern, kita beranjak ke proses yang menggerakkan evolusi bahasa — Kotlin Evolution and Review Process (KEEP) — yang mengatur bagaimana fitur baru diusulkan dan distabilkan.