Cara Kerja Kotlin

Kompilasi Kotlin menjadi bytecode JVM, model kompilasinya, serta hubungan bahasa ini dengan Java.

Daftar isi

Kode sumber Kotlin tidak dijalankan langsung oleh sistem operasi. Compiler mengubahnya menjadi bytecode JVM, dan bytecode itulah yang dieksekusi oleh JVM. Topik ini menjelaskan alur kompilasi Kotlin, memeriksa hasil kompilasinya dengan alat bawaan JDK, serta menunjukkan hubungan Kotlin dengan Java melalui contoh yang benar-benar dijalankan.

Dari kode sumber ke bytecode#

Program Kotlin dimulai sebagai berkas teks berakhiran .kt. Compiler kotlinc membaca berkas tersebut, memeriksa kebenaran tipenya, lalu menghasilkan bytecode, yaitu rangkaian instruksi berformat .class yang menjadi bahasa mesin JVM. JVM kemudian menjalankan bytecode tersebut seperti menjalankan hasil kompilasi Java. Karena target akhirnya sama, program Kotlin dan Java berbagi runtime yang sama dan dapat memakai library Java yang sama tanpa penyesuaian. Bytecode yang sama pula dapat dijalankan pada JVM di sistem operasi mana pun, sehingga program Kotlin tidak perlu dikompilasi ulang untuk tiap sistem operasi.

Alur lengkapnya dirangkum pada diagram berikut.

Berkas .ktkode sumberkotlinccompiler Kotlinbytecodeberkas .classJVMmenjalankan program
Alur kompilasi Kotlin dari kode sumber sampai JVM

Alur ini menyerupai jalur kerja javac pada program Java. Yang berbeda hanya bahasa sumbernya, sedangkan keluaran compiler berupa bytecode dengan format yang sama. Konsekuensinya, hasil kompilasi Kotlin dan hasil kompilasi Java dapat diletakkan dalam satu aplikasi tanpa penerjemah di antara keduanya.

Memeriksa hasil kompilasi#

Mekanisme ini dapat diperiksa langsung dengan alat yang tersedia. Berkas berikut berisi satu fungsi top-level, yaitu fungsi yang dideklarasikan di luar kelas mana pun. Perhatikan bahwa berkasnya tidak memuat deklarasi kelas sama sekali.

kotlin Bilangan.kt
// Fungsi top-level yang dikompilasi menjadi kelas BilanganKt
fun jumlah(a: Int, b: Int): Int = a + b
bash
# mengompilasi Bilangan.kt menjadi berkas bil.jar
kotlinc Bilangan.kt -d bil.jar
# menampilkan daftar isi bil.jar
jar tf bil.jar
text Keluaran
META-INF/MANIFEST.MF
BilanganKt.class
META-INF/main.kotlin_module

Perintah jar tf menampilkan daftar isi berkas jar. Di dalamnya terdapat BilanganKt.class, satu berkas kelas bytecode yang dihasilkan dari berkas sumber di atas, beserta META-INF/main.kotlin_module, metadata internal Kotlin yang membantu compiler membaca kode Kotlin lain yang memakai berkas ini. Bentuk kelas tersebut dapat dibongkar dengan alat javap milik JDK.

bash
# membongkar tampilan kelas BilanganKt dengan javap
javap -cp bil.jar BilanganKt
text Keluaran
Compiled from "Bilangan.kt"
public final class BilanganKt {
  public static final int jumlah(int, int);
}

Keluaran javap memperlihatkan fungsi jumlah sebagai metode statis milik kelas final BilanganKt. Inilah wujud nyata kode Kotlin di atas JVM, dan wujud inilah yang dilihat program Java ketika memanggil fungsi tersebut.

Model kompilasi dua tahap#

Di dalam kotlinc, proses kompilasi terbagi dua tahap besar. Tahap pertama adalah frontend, yaitu bagian yang membaca kode sumber, menyusun pohon sintaks, dan memeriksa seluruh aturan tipe. Apabila ditemukan kesalahan, misalnya pemanggilan fungsi dengan argumen yang tidak cocok, proses berhenti dan compiler menampilkan pesan error. Tahap kedua adalah backend, yaitu bagian yang mengubah hasil pemeriksaan menjadi kode untuk target tertentu, misalnya bytecode JVM, kode JavaScript, atau biner native.

Sejak versi 2.0, kedua tahap tersebut dijalankan oleh generasi compiler baru bernama K2 yang menggantikan arsitektur sebelumnya. Hasilnya berupa kompilasi yang lebih cepat dan pemeriksaan tipe yang lebih konsisten, sedangkan cara pemakaiannya dari terminal maupun IDE tidak berubah.

Pemisahan ini juga menjelaskan perilaku kotlinc yang terlihat sehari-hari. Pesan error muncul sebelum kode apa pun dihasilkan, lengkap dengan nama berkas dan nomor baris sumbernya, sedangkan kompilasi yang berhasil tidak mencetak apa pun. Backend yang sama dipakai untuk seluruh program JVM, sehingga bentuk bytecode hasil kotlinc konsisten untuk proyek mana pun.

Interoperabilitas dengan Java#

Karena keluaran kompilasinya berupa bytecode yang sama, kode Kotlin dan Java dapat saling dipanggil secara langsung. Banyak proyek mengadopsi Kotlin lewat jalur ini, yaitu menambah berkas Kotlin pada proyek Java yang sudah berjalan. Arah pertama adalah program Java yang memanggil fungsi Kotlin. Berkas berikut memanggil fungsi jumlah dari berkas Bilangan.kt. Namanya persis seperti yang dilaporkan javap, yaitu BilanganKt.jumlah.

java Pemakai.java
// tidak-dijalankan
// dikompilasi dan dijalankan melalui blok bash pada bagian ini
public class Pemakai {
    public static void main(String[] args) {
        System.out.println("Jumlah 12 dan 30: " + BilanganKt.jumlah(12, 30));
    }
}
bash
# mengompilasi Pemakai.java dengan bil.jar pada classpath
javac -cp bil.jar Pemakai.java
# menjalankan program Java yang memanggil fungsi Kotlin
java -cp "bil.jar:." Pemakai
text Keluaran
Jumlah 12 dan 30: 42

Perintah javac mengompilasi program Java dengan bil.jar diletakkan pada classpath, yaitu daftar lokasi tempat JVM mencari kelas yang dibutuhkan program. Hasilnya, kode Java dan kode Kotlin berjalan dalam satu proses JVM yang sama tanpa jembatan tambahan.

Arah sebaliknya juga berlaku. Kode Kotlin dapat memakai seluruh kelas Java tanpa pembungkus khusus. Program berikut memakai kelas java.util.TreeMap dari library standar Java, yaitu implementasi Map yang otomatis mengurutkan kuncinya.

kotlin UrutanKata.kt
// Memakai kelas Java TreeMap langsung dari Kotlin
import java.util.TreeMap

fun main() {
    val urut = TreeMap<String, Int>()
    urut["dua"] = 2
    urut["satu"] = 1
    urut["tiga"] = 3
    for ((kunci, nilai) in urut) {
        println("$kunci = $nilai")
    }
}
bash
# mengompilasi UrutanKata.kt menjadi jar yang dapat dijalankan sendiri
kotlinc UrutanKata.kt -include-runtime -d urut.jar
# menjalankan program
java -jar urut.jar
text Keluaran
dua = 2
satu = 1
tiga = 3

Kelas Java dipakai persis seperti kelas Kotlin, termasuk pengambilan nilainya melalui tanda kurung siku. Interoperabilitas dua arah semacam ini memungkinkan proyek Java yang sudah berjalan dipindahkan ke Kotlin secara bertahap, satu berkas demi satu berkas.

Ringkasan#

  • Compiler mengubah berkas .kt menjadi bytecode berformat .class yang dijalankan JVM, target yang sama dengan hasil javac.
  • Fungsi top-level pada berkas Bilangan.kt menjadi metode statis pada kelas BilanganKt, sebagaimana diperlihatkan jar tf dan javap.
  • Kompilasi terbagi dua tahap, yaitu frontend untuk pemeriksaan tipe dan backend untuk pembangkitan kode per target, dan sejak versi 2.0 keduanya dijalankan compiler K2.
  • Kotlin dan Java saling dipanggil dua arah dalam satu proses JVM tanpa lapisan tambahan.

Referensi#