Kode Java memanggil kode Kotlin hanya melalui kelas-kelas hasil kompilasi yang berada di classpath, karena Java tidak mengetahui bahasa Kotlin secara langsung. Topik ini menunjukkan nama kelas dan metode yang dihasilkan compiler untuk fungsi top-level dan companion object, cara membacanya dengan javap, serta anotasi @JvmStatic dan @JvmOverloads yang membuat hasil kompilasi lebih ramah bagi pemanggil Java.
Fungsi top-level menjadi metode static#
Kotlin mengizinkan fungsi ditulis tanpa kelas, sedangkan JVM mengenal hanya kelas dan metode. Compiler menjembatani perbedaan itu dengan aturan penamaan: berkas Bilangan.kt dikompilasi menjadi kelas BilanganKt, dan setiap fungsi top-level pada berkas itu menjadi metode static di dalamnya.
// Fungsi top-level pada berkas Bilangan.kt
fun tambah(a: Int, b: Int): Int = a + bProgram Java berikut memanggil fungsi tersebut dengan menulis nama kelas hasil kompilasi secara eksplisit.
// tidak-dijalankan
// Memanggil fungsi top-level Kotlin dari Java
public class Pemakai {
public static void main(String[] args) {
int hasil = BilanganKt.tambah(7, 5);
System.out.println("Hasil: " + hasil);
}
}Rantai perintah berikut mengompilasi berkas Kotlin menjadi jar, mengompilasi pemanggil Java dengan jar tersebut pada classpath, menjalankannya, lalu membuka isi kelas BilanganKt dengan javap.
export JAVA_HOME=/home/kal/my-project/tools/jdk-merged
export PATH=$JAVA_HOME/bin:/home/kal/my-project/tools/kotlinc/bin:$PATH
export STDLIB=/home/kal/my-project/tools/kotlinc/lib/kotlin-stdlib.jar
# mengompilasi berkas Kotlin menjadi jar
kotlinc Bilangan.kt -d bil.jar
# mengompilasi pemanggil Java dengan jar Kotlin pada classpath
javac -cp "bil.jar:$STDLIB" Pemakai.java
# menjalankan program Java
java -cp "bil.jar:$STDLIB:." Pemakai
# membaca struktur kelas yang dihasilkan compiler
javap -p -cp bil.jar BilanganKtHasil: 12
Compiled from "Bilangan.kt"
public final class BilanganKt {
public static final int tambah(int, int);
}Bagian kedua keluaran berasal dari javap, alat bawaan JDK yang menampilkan struktur berkas kelas. Keluarannya menegaskan dua hal, yaitu kelas BilanganKt berbentuk final dan metode tambah berbentuk static final. Tanda -p menambahkan anggota non-publik pada daftar, sehingga keluaran javap berikutnya dapat dibaca tanpa kehilangan informasi.
@JvmStatic pada companion object#
Companion object pada Kotlin menjadi field Companion bertipe kelas tersendiri, dan fungsi di dalamnya menjadi metode instans pada kelas tersebut. Pemanggil dari Java mengikuti bentuk itu dengan menulis Konversi.Companion.keKelvin. Anotasi @JvmStatic mengubah keadaan ini dengan menambahkan metode static pada kelas luar, sehingga pemanggil Java cukup menulis Konversi.keFahrenheit.
// Companion object dengan @JvmStatic
class Konversi {
companion object {
@JvmStatic
fun keFahrenheit(celsius: Double): Double = celsius * 9 / 5 + 32
fun keKelvin(celsius: Double): Double = celsius + 273.15
}
}Program Java berikut sengaja memanggil kedua fungsi dengan gaya yang berbeda agar perbedaannya terlihat.
// tidak-dijalankan
// Membandingkan pemanggilan dengan dan tanpa @JvmStatic
public class PemakaiKonversi {
public static void main(String[] args) {
System.out.println(Konversi.keFahrenheit(30.0));
System.out.println(Konversi.Companion.keKelvin(30.0));
}
}export JAVA_HOME=/home/kal/my-project/tools/jdk-merged
export PATH=$JAVA_HOME/bin:/home/kal/my-project/tools/kotlinc/bin:$PATH
export STDLIB=/home/kal/my-project/tools/kotlinc/lib/kotlin-stdlib.jar
kotlinc Konversi.kt -d konversi.jar
# metode static hadir pada kelas Konversi berkat @JvmStatic
javap -p -cp konversi.jar Konversi
# tanpa @JvmStatic, metode hanya ada pada kelas Companion
javap -p -cp konversi.jar 'Konversi$Companion'
javac -cp "konversi.jar:$STDLIB" PemakaiKonversi.java
java -cp "konversi.jar:$STDLIB:." PemakaiKonversiCompiled from "Konversi.kt"
public final class Konversi {
public static final Konversi$Companion Companion;
public Konversi();
public static final double keFahrenheit(double);
static {};
}
Compiled from "Konversi.kt"
public final class Konversi$Companion {
private Konversi$Companion();
public final double keFahrenheit(double);
public final double keKelvin(double);
public Konversi$Companion(kotlin.jvm.internal.DefaultConstructorMarker);
}
86.0
303.15Keluaran javap yang pertama memperlihatkan metode keFahrenheit pada kelas Konversi sebagai metode static, hasil tambahan dari @JvmStatic. Keluaran yang kedua memperlihatkan kelas Konversi$Companion yang tetap memuat kedua fungsi sebagai metode instans, termasuk keKelvin yang tidak diberi anotasi. Dua baris terakhir adalah hasil eksekusi program Java, yaitu 86.0 untuk konversi 30 derajat Celsius ke Fahrenheit dan 303.15 untuk konversi ke Kelvin.
@JvmOverloads untuk argumen bawaan#
Argumen bawaan Kotlin tidak memiliki padanan pada Java, sehingga secara bawaan compiler menghasilkan satu metode yang mewajibkan seluruh parameter diisi. Pemanggil Java terpaksa menuliskan nilai untuk setiap parameter, termasuk yang di Kotlin boleh dilewatkan. Anotasi @JvmOverloads mengatasi hal ini dengan menghasilkan satu overload untuk setiap parameter bawaan yang dilepas, mulai dari versi lengkap sampai versi dengan parameter tersembunyi paling banyak.
// Argumen bawaan dibungkus @JvmOverloads
@JvmOverloads
fun ringkas(judul: String, jumlah: Int = 10): String = "$judul ($jumlah berkas)"export JAVA_HOME=/home/kal/my-project/tools/jdk-merged
export PATH=$JAVA_HOME/bin:/home/kal/my-project/tools/kotlinc/bin:$PATH
kotlinc Laporan.kt -d laporan.jar
# @JvmOverloads menghasilkan satu metode untuk tiap kombinasi argumen
javap -p -cp laporan.jar LaporanKtCompiled from "Laporan.kt"
public final class LaporanKt {
public static final java.lang.String ringkas(java.lang.String, int);
public static java.lang.String ringkas$default(java.lang.String, int, int, java.lang.Object);
public static final java.lang.String ringkas(java.lang.String);
}Keluaran memperlihatkan dua overload ringkas, yaitu ringkas(String, int) dan ringkas(String). Pemanggil Java kini dapat memilih keduanya sebagaimana contoh berikut.
// tidak-dijalankan
String satu = LaporanKt.ringkas("Arsip 2024");
String dua = LaporanKt.ringkas("Arsip 2024", 25);Metode ringkas$default pada keluaran adalah metode sintetis yang digunakan compiler Kotlin sendiri untuk meneruskan nilai bawaan, sehingga tidak ditujukan bagi pemanggil Java. Overload buatan @JvmOverloads bertambah satu untuk setiap parameter bawaan berurutan dari akhir, sehingga fungsi dengan dua parameter bawaan menghasilkan tiga overload.
Ringkasan#
- Fungsi top-level pada berkas
Nama.ktmenjadi metode static pada kelasNamaKt, dan pemanggil Java menyebut kelas tersebut secara eksplisit. javap -pmenampilkan nama kelas dan metode hasil kompilasi, sehingga nama yang dihasilkan compiler dapat diverifikasi sebelum dipanggil dari Java.@JvmStaticmenambahkan metode static pada kelas luar companion object, sedangkan@JvmOverloadsmenghasilkan overload untuk setiap parameter bawaan yang dilepas.- Kedua anotasi tersebut tidak mengubah perilaku di sisi Kotlin, melainkan menambah bentuk baru pada bytecode untuk pemanggil Java.
Referensi#
- Calling Kotlin from Java — JetBrains
— Dokumentasi resmi pemanggilan Kotlin dari Java, termasuk fungsi top-level,
@JvmStatic, dan@JvmOverloads. - javap — Oracle
— Manual alat
javappada JDK 21 untuk menampilkan deklarasi berkas kelas.