Interoperabilitas tidak hanya satu arah. Kode Java juga dapat memanggil kode Kotlin — namun karena Java tidak mengenal top-level function, default argumen, atau companion object, kompilator Kotlin harus menerjemahkan semua itu menjadi konstruksi Java yang setara. Bab ini menjelaskan tiga aturan translasi: top-level function menjadi FileNameKt.methodName(), companion object menjadi ClassName.Companion (atau @JvmStatic untuk static sejati), dan default argumen memerlukan @JvmOverloads agar Java dapat memanggil tanpa semua argumen.

Tiga Aturan Translasi #

Konstruk KotlinPadanan Java DefaultAnotasi yang Membantu
Top-level function fun sapa(...)FileNameKt.sapa(...)@file:JvmName("Util") (chapter @JvmName)
companion objectClassName.Companion.INSTANCE.method()@JvmStatic
Default argumen fun f(a: Int, b: Int = 0)hanya satu signature f(int,int)@JvmOverloads menghasilkan beberapa overload
Top-level property val X = ...FileNameKt.getX()@JvmField (chapter @JvmField dan @JvmStatic)

Kode Java yang memanggil Kotlin harus mematuhi translasi ini. Bab ini berfokus pada tiga aturan yang paling sering dijumpai di proyek nyata.

Contoh Lengkap: Top-Level, Object, Companion, Default Arg #

Kode berikut mendefinisikan lima konstruk Kotlin dan menampilkan padanan Java yang akan dipanggil dari sisi Java:

kotlin
// Demonstrasi bagaimana kode Kotlin dilihat dari sisi Java:
// - Top-level function menjadi static method pada file class FileNameKt
// - @JvmStatic menghasilkan static method sejati pada companion object
// - @JvmOverloads menghasilkan beberapa overload untuk default arg

// 1. Top-level function: di Java menjadi UtilKt.sapa("Budi")
fun sapa(nama: String): String = "Halo, $nama!"

// 2. Top-level function dengan default arg: perlu @JvmOverloads agar Java bisa panggil tanpa arg ke-2
@JvmOverloads
fun hitungDiskon(harga: Int, persen: Int = 10, minimum: Int = 0): Int {
    val diskon = (harga * persen) / 100
    return if (diskon > minimum) diskon else minimum
}

// 3. Object singleton: di Java menjadi Util.INSTANCE
object Penanda {
    const val VERSI: String = "1.0.0"

    fun tandaVersi(): String = "v$VERSI"
}

// 4. Companion object dengan @JvmStatic agar dapat dipanggil sebagai static method sejati
class Matematika {
    companion object {
        @JvmStatic
        fun kuadrat(x: Int): Int = x * x

        @JvmStatic
        val KONSTANTA_PI: Double = 3.14
    }
}

// 5. Top-level property: di Java menjadi UtilKt.getNamaAplikasi()
const val NAMA_APLIKASI: String = "BelajarKotlin"

fun main() {
    // 1. Top-level function
    println("=== Top-level function ===")
    println("sapa(\"Budi\")    : ${sapa("Budi")}")

    // 2. Default argumen (Kotlin bisa panggil tanpa @JvmOverloads)
    println()
    println("=== Default argumen ===")
    println("hitungDiskon(100000)              : ${hitungDiskon(100000)}")
    println("hitungDiskon(100000, 20)          : ${hitungDiskon(100000, 20)}")
    println("hitungDiskon(100000, 20, 5000)    : ${hitungDiskon(100000, 20, 5000)}")

    // 3. Object singleton
    println()
    println("=== Object singleton ===")
    println("Penanda.VERSI                  : ${Penanda.VERSI}")
    println("Penanda.tandaVersi()           : ${Penanda.tandaVersi()}")

    // 4. Companion object dengan @JvmStatic
    println()
    println("=== Companion + @JvmStatic ===")
    println("Matematika.kuadrat(5)          : ${Matematika.kuadrat(5)}")
    println("Matematika.KONSTANTA_PI        : ${Matematika.KONSTANTA_PI}")

    // 5. Top-level property
    println()
    println("=== Top-level property ===")
    println("NAMA_APLIKASI                  : $NAMA_APLIKASI")

    // Simulasi pemanggilan dari sisi Java (yang akan memakai):
    //   UtilKt.sapa("Budi")
    //   UtilKt.hitungDiskon(100000)
    //   UtilKt.hitungDiskon(100000, 20)
    //   UtilKt.hitungDiskon(100000, 20, 5000)
    //   Penanda.INSTANCE.tandaVersi()
    //   Matematika.kuadrat(5)         <-- @JvmStatic, no INSTANCE
    //   UtilKt.getNAMA_APLIKASI()
    println()
    println("=== Padanan Java ===")
    println("// Sisi Java yang memanggil:")
    println("//   Calling_kotlin_from_javaKt.sapa(\"Budi\")")
    println("//   Calling_kotlin_from_javaKt.hitungDiskon(100000)")
    println("//   Calling_kotlin_from_javaKt.hitungDiskon(100000, 20)")
    println("//   Calling_kotlin_from_javaKt.hitungDiskon(100000, 20, 5000)")
    println("//   Penanda.INSTANCE.tandaVersi()")
    println("//   Matematika.kuadrat(5)         <-- @JvmStatic, tanpa INSTANCE")
    println("//   Calling_kotlin_from_javaKt.getNAMA_APLIKASI()")
}

Output yang Dihasilkan:

text
=== Top-level function ===
sapa("Budi")    : Halo, Budi!

=== Default argumen ===
hitungDiskon(100000)              : 10000
hitungDiskon(100000, 20)          : 20000
hitungDiskon(100000, 20, 5000)    : 20000

=== Object singleton ===
Penanda.VERSI                  : 1.0.0
Penanda.tandaVersi()           : v1.0.0

=== Companion + @JvmStatic ===
Matematika.kuadrat(5)          : 25
Matematika.KONSTANTA_PI        : 3.14

=== Top-level property ===
NAMA_APLIKASI                  : BelajarKotlin

=== Padanan Java ===
// Sisi Java yang memanggil:
//   Calling_kotlin_from_javaKt.sapa("Budi")
//   Calling_kotlin_from_javaKt.hitungDiskon(100000)
//   Calling_kotlin_from_javaKt.hitungDiskon(100000, 20)
//   Calling_kotlin_from_javaKt.hitungDiskon(100000, 20, 5000)
//   Penanda.INSTANCE.tandaVersi()
//   Matematika.kuadrat(5)         <-- @JvmStatic, tanpa INSTANCE
//   Calling_kotlin_from_javaKt.getNAMA_APLIKASI()

Mari kita telaah baris-baris kode di atas:

  1. Baris 7 (fun sapa(nama: String): String): top-level function. Kompilator membuat class bernama Calling_kotlin_from_javaKt (berdasarkan nama file calling_kotlin.kt dengan kapitalisasi pertama, underscore dipertahankan, akhiran Kt). Sisi Java memanggil Calling_kotlin_from_javaKt.sapa("Budi") sebagai static method.
  2. Baris 11 (@JvmOverloads fun hitungDiskon(...)): tanpa @JvmOverloads, Java hanya melihat satu signature hitungDiskon(int,int,int) dan tidak dapat memakai default argumen. Anotasi ini menghasilkan tiga overload Java: hitungDiskon(int), hitungDiskon(int,int), dan hitungDiskon(int,int,int).
  3. Baris 18-21 (object Penanda): object di Kotlin menjadi singleton class di Java. Sisi Java mengakses instance lewat Penanda.INSTANCE. Method tandaVersi() dipanggil sebagai Penanda.INSTANCE.tandaVersi(). Konstanta VERSI (ditandai const) menjadi public static final sejati.
  4. Baris 26-31 (companion object dengan @JvmStatic): tanpa @JvmStatic, Java harus menulis Matematika.Companion.kuadrat(5). Dengan @JvmStatic, kompilator membuat static method sejati Matematika.kuadrat(5) tanpa perantara Companion. Field KONSTANTA_PI yang ditandai @JvmStatic juga menjadi static accessor sejati.
  5. Baris 35 (const val NAMA_APLIKASI): kata kunci const membuat properti menjadi public static final String. Sisi Java membaca Calling_kotlin_from_javaKt.NAMA_APLIKASI (bukan getter, karena const membuat field langsung). Tanpa const, aksesnya via getter getNAMA_APLIKASI().
  6. Baris 58-66 (print padanan Java): komentar ini menjelaskan translasi yang terjadi di bytecode. Pemanggilan dari Java persis seperti komentar menunjukkan — kompilator Java tidak tahu bahwa kode aslinya adalah Kotlin.

Strategi Anotasi untuk Library Kotlin yang Dipakai Java #

Bila kita membangun library Kotlin yang akan dipakai dari Java, anotasi berikut sebaiknya menjadi default:

KasusAnotasiManfaat
Top-level function dipanggil Java@file:JvmName("Util")Nama pendek & deskriptif
Method di companion object dipanggil Java@JvmStaticPanggilan langsung tanpa .Companion
Function dengan default argumen@JvmOverloadsJava dapat panggil tanpa semua arg
Konstanta publikconst val (atau @JvmField)Akses langsung tanpa getter

Tanpa anotasi ini, kode Java yang memanggil Kotlin akan tampak berantakan dan tidak idiomatik. Library yang baik untuk konsumsi Java harus membungkus detail ini di level API publik.

Referensi Lanjutan #

Selanjutnya #

Top-level function Kotlin yang dipanggil dari Java mengembalikan tipe yang tidak Kotlin ketahui nullability-nya. Konsep platform types inilah yang menjadi sumber NullPointerException tersekat di kode hybrid Java+Kotlin. Bab Platform Types akan membahasnya tuntas.