Kotlin memungkinkan default argumen pada parameter function: fun f(a: Int, b: Int = 0). Pemanggil Kotlin dapat memanggil f(5) atau f(5, 10) — kompilator menyisipkan nilai default. Java tidak memiliki fitur ini, sehingga method Kotlin dengan default argumen hanya menghasilkan satu signature Java dengan semua parameter wajib. Anotasi @JvmOverloads mengatasi keterbatasan ini dengan menghasilkan beberapa overload Java secara otomatis. Bab ini membahas cara kerja, jumlah overload yang dihasilkan, dan strategi pemakaiannya.
Aturan Jumlah Overload #
Untuk function dengan n parameter default, @JvmOverloads menghasilkan n + 1 overload. Aturan ini berlaku untuk function top-level, constructor, dan method instance.
| Function Kotlin | Jumlah Overload Java |
|---|---|
fun f(a, b = 0, c = 1) (2 default) | 3 |
fun f(a, b = 0, c = 1, d = 2) (3 default) | 4 |
fun f(a) (0 default) | 1 (sama seperti tanpa anotasi) |
fun f(a = 0, b = 0) (semua default) | 3 |
Overload yang dihasilkan mengikuti urutan parameter: parameter terkanan yang default-nya disisipkan terlebih dahulu. Untuk fun f(a, b = 0, c = 1), overload yang dihasilkan adalah f(a), f(a, b), f(a, b, c). Kita tidak bisa membuat f(a, c) (skip b) — Java tidak mendukung named argumen.
Contoh Lengkap: Top-Level, Constructor, Method Instance #
Kode berikut mendemonstrasikan @JvmOverloads pada tiga konteks: top-level function, constructor, dan method instance:
// Demonstrasi @JvmOverloads: menghasilkan beberapa overload Java untuk default argumen Kotlin
//
// Kotlin: fun f(a: Int, b: Int = 0, c: Int = 1)
// Java tanpa @JvmOverloads: hanya f(int,int,int) tersedia
// Java dengan @JvmOverloads: f(int), f(int,int), f(int,int,int) tersedia
// 1. TANPA @JvmOverloads: Java harus memberi semua argumen
fun hitungTotalTanpaAnotasi(harga: Int, persen: Int = 10, ongkir: Int = 5000): Int {
return harga + (harga * persen) / 100 + ongkir
}
// 2. DENGAN @JvmOverloads: Java dapat memanggil 3 variasi
@JvmOverloads
fun hitungTotalDenganAnotasi(harga: Int, persen: Int = 10, ongkir: Int = 5000): Int {
return harga + (harga * persen) / 100 + ongkir
}
// 3. @JvmOverloads pada constructor
data class Produk @JvmOverloads constructor(
val nama: String,
val harga: Int = 0,
val kategori: String = "umum",
val stok: Int = 0,
)
// 4. @JvmOverloads pada method instance
class Keranjang(val items: MutableList<String> = mutableListOf()) {
@JvmOverloads
fun tambah(nama: String, jumlah: Int = 1, catatan: String = ""): Boolean {
repeat(jumlah) { items.add("$nama#$catatan") }
return true
}
}
// 5. Aturan jumlah overload yang dihasilkan:
// bila ada n parameter default, akan ada n+1 overload.
// Misal: 3 parameter default -> 4 signature.
fun main() {
println("=== Tanpa @JvmOverloads (Kotlin panggil bisa pakai default) ===")
println("hitungTotalTanpaAnotasi(100000) : ${hitungTotalTanpaAnotasi(100000)}")
println("hitungTotalTanpaAnotasi(100000, 20) : ${hitungTotalTanpaAnotasi(100000, 20)}")
println("hitungTotalTanpaAnotasi(100000, 20, 0) : ${hitungTotalTanpaAnotasi(100000, 20, 0)}")
// Di Java: HANYA hitungTotalTanpaAnotasi(100000, 10, 5000) yang tersedia
println()
println("=== Dengan @JvmOverloads ===")
println("hitungTotalDenganAnotasi(100000) : ${hitungTotalDenganAnotasi(100000)}")
println("hitungTotalDenganAnotasi(100000, 20) : ${hitungTotalDenganAnotasi(100000, 20)}")
println("hitungTotalDenganAnotasi(100000, 20, 0) : ${hitungTotalDenganAnotasi(100000, 20, 0)}")
// Di Java: ketiga signature di atas tersedia
println()
println("=== @JvmOverloads pada constructor ===")
val p1 = Produk("Buku Kotlin")
val p2 = Produk("Laptop", 15000000)
val p3 = Produk("Mouse", 250000, "elektronik")
val p4 = Produk("Keyboard", 750000, "elektronik", 10)
println("p1: $p1")
println("p2: $p2")
println("p3: $p3")
println("p4: $p4")
// Di Java: 4 constructor tersedia (4,3,2,1 argumen)
println()
println("=== @JvmOverloads pada method instance ===")
val k = Keranjang()
k.tambah("Pensil")
k.tambah("Buku", 3)
k.tambah("Pulpen", 2, "warna biru")
println("items: ${k.items}")
// Di Java: keranjang.tambah("Pensil"), .tambah("Buku", 3), .tambah("Pulpen", 2, "warna biru")
println()
println("=== Jumlah overload yang dihasilkan ===")
println("// Produk(nama, harga=0, kategori=\"umum\", stok=0)")
println("// -> 4 signature: (String), (String,int), (String,int,String), (String,int,String,int)")
println("// hitungTotalDenganAnotasi(harga, persen=10, ongkir=5000)")
println("// -> 3 signature: (int), (int,int), (int,int,int)")
}Output yang Dihasilkan:
=== Tanpa @JvmOverloads (Kotlin panggil bisa pakai default) ===
hitungTotalTanpaAnotasi(100000) : 115000
hitungTotalTanpaAnotasi(100000, 20) : 125000
hitungTotalTanpaAnotasi(100000, 20, 0) : 120000
=== Dengan @JvmOverloads ===
hitungTotalDenganAnotasi(100000) : 115000
hitungTotalDenganAnotasi(100000, 20) : 125000
hitungTotalDenganAnotasi(100000, 20, 0) : 120000
=== @JvmOverloads pada constructor ===
p1: Produk(nama=Buku Kotlin, harga=0, kategori=umum, stok=0)
p2: Produk(nama=Laptop, harga=15000000, kategori=umum, stok=0)
p3: Produk(nama=Mouse, harga=250000, kategori=elektronik, stok=0)
p4: Produk(nama=Keyboard, harga=750000, kategori=elektronik, stok=10)
=== @JvmOverloads pada method instance ===
items: [Pensil#, Buku#, Buku#, Buku#, Pulpen#warna biru, Pulpen#warna biru]
=== Jumlah overload yang dihasilkan ===
// Produk(nama, harga=0, kategori="umum", stok=0)
// -> 4 signature: (String), (String,int), (String,int,String), (String,int,String,int)
// hitungTotalDenganAnotasi(harga, persen=10, ongkir=5000)
// -> 3 signature: (int), (int,int), (int,int,int)Mari kita telaah baris-baris kode di atas:
- Baris 7 (
fun hitungTotalTanpaAnotasi(harga, persen=10, ongkir=5000)): tanpa@JvmOverloads, bytecode Java hanya menghasilkan satu signatureint hitungTotalTanpaAnotasi(int, int, int). Pemanggil Java harus selalu menyediakan ketiga argumen. Pemanggil Kotlin dapat memakai default argumen karena kompilator Kotlin menyisipkan nilai default di call site. - Baris 13 (
@JvmOverloads fun hitungTotalDenganAnotasi(...)): anotasi menghasilkan tiga overload Java —hitungTotalDenganAnotasi(int),hitungTotalDenganAnotasi(int, int),hitungTotalDenganAnotasi(int, int, int). Setiap overload memanggil overload lengkap dengan nilai default untuk parameter yang tidak disebut. - Baris 19-24 (
data class Produk @JvmOverloads constructor(...)):@JvmOverloadspada constructor menghasilkan empat constructor Java. Setiap constructor memanggil constructor lengkap dengan nilai default. Data class tetap menghasilkanequals,hashCode,toString, dancopyseperti biasa. - Baris 27-33 (
Keranjang.tambah(...)dengan@JvmOverloads): method instance juga mendukung anotasi ini. Tiga overload Java dihasilkan:tambah(String),tambah(String, int),tambah(String, int, String). Setiap overload mendelegasikan ke overload lengkap. - Baris 41-43 (pemanggilan tanpa anotasi): meski tanpa
@JvmOverloads, pemanggil Kotlin tetap dapat memakai default argumen — ini fitur level bahasa Kotlin. Anotasi hanya berdampak pada visibilitas Java. - Baris 58-62 (pemanggilan dengan berbagai argumen):
Produk("Buku Kotlin")memakai 3 default argumen,Produk("Laptop", 15000000)memakai 2 default, dan seterusnya. Semua valid di sisi Kotlin;@JvmOverloadsmemastikan semuanya juga valid di sisi Java.
Kapan Memakai @JvmOverloads #
| Skenario | Pakai @JvmOverloads? | Alasan |
|---|---|---|
| Library Kotlin dipanggil Java, banyak default argumen | Ya | Java mendapat API idiomatik |
| Library Kotlin murni untuk Kotlin | Tidak perlu | Default argumen sudah cukup |
| Constructor dengan 5+ default argumen | Pertimbangkan | 5+ overload dapat memperbesar bytecode |
| Method yang jarang dipanggil Java | Tidak perlu | Tidak ada manfaat, hanya menambah bytecode |
Referensi Lanjutan #
- Calling Java from Kotlin
— dokumentasi Java interop Kotlin untuk anotasi
@JvmOverloads - Calling Kotlin from Java — panduan mengekspos overload Kotlin ke pemanggil Java
Selanjutnya #
Selain @JvmOverloads, ada anotasi @JvmName yang dapat mengubah nama method atau file class yang dihasilkan. Bab berikutnya membahas penggunaannya untuk disambiguasi, penamaan idiomatik, dan kasus khusus seperti type erasure pada List<String> vs List<Int>.