Episode ini menguasai interoperabilitas Kotlin dan Java: memanggil Java dari Kotlin dan sebaliknya, bekerja dengan codebase Java legacy, nullability annotations dan keamanan tipe lintas batas, serta strategi memigrasi project Java ke Kotlin secara bertahap tanpa menghentikan pengembangan.

Kotlin tidak memaksa kalian meninggalkan Java. Episode 17 membahas interoperability: memanggil kode Java dari Kotlin dan sebaliknya, bekerja di codebase Java yang sudah lama, menjaga keamanan tipe lintas batas, dan memigrasi project secara bertahap.
Ini alasan utama banyak perusahaan memilih Kotlin: adopsi bisa incremental. Satu file Java diubah menjadi Kotlin, aplikasi tetap berjalan, dan keuntungan terasa pelan-pelan tanpa big bang rewrite.
Setelah episode ini, kalian akan memigrasi project Java ke Kotlin dengan strategi yang aman dan tidak menghentikan fitur baru.
Karena Kotlin dikompilasi ke bytecode JVM, class Java terlihat natural dari Kotlin:
import java.time.LocalDate
import java.util.UUID
val id = UUID.randomUUID()
val hari = LocalDate.now()UUID dan LocalDate adalah class Java yang dipakai langsung dari Kotlin tanpa adapter. Seluruh standard library Java terbuka untuk project Kotlin — ini alasan Kotlin bisa memakai ekosistem JVM yang sangat besar.
Properties Kotlin otomatis memetakan ke getter dan setter Java. Field private Java tetap private; akses lewat method. Konsekuensi kecil: sebuah method getNama() di Java terlihat sebagai property nama dari sisi Kotlin, membuat interop terasa mulus.
Ketika Java memanggil Kotlin, tipe Kotlin menjadi platform types — Kotlin tidak tahu apakah Java memperlakukan nilai sebagai nullable atau tidak. Compiler memberi peringatan fleksibel, tapi kalian harus menangani null dengan bijak di sisi Java:
String nama = KtSapa.sapa("Budi");Fungsi Kotlin fun sapa(nama: String): String terlihat sebagai method statis di class KtSapa (dengan akhiran Kt). Nilai kembaliannya menjadi platform type: Java bisa memanggil langsung, tapi null tidak dijamin aman di sisi Kotlin ketika nilai kembali.
Anotasi @JvmStatic, @JvmOverloads, dan @JvmField memberi kontrol bagaimana kode Kotlin diekspos ke Java:
class Util {
companion object {
@JvmStatic
fun bantu(): String = "helper"
@JvmField
val versi = "1.0"
}
}@JvmStatic menjadikan fungsi accessible sebagai method statis (bukan Companion.bantu()), dan @JvmField mengekspos field langsung tanpa getter. @JvmOverloads membangkitkan overload Java untuk parameter default. Anotasi ini membuat API Kotlin ramah bagi konsumen Java.
Masalah utama interop adalah nullability. Kotlin menegakkan null safety; Java tidak. Ketika Java mengembalikan nilai yang bisa null, Kotlin melihatnya sebagai platform type — tidak tahu pasti. Solusinya: anotasi nullability dari sisi Java:
// Java: @Nullable String cari()
val hasil = service.cariNama()
val aman = hasil?.uppercase() ?: "tidak ada"hasil adalah platform type yang bisa null. Kalian menangani dengan safe call dan elvis seperti biasa. Untuk mengurangi ambiguitas, library memakai anotasi @Nullable dan @NotNull (dari javax.annotation, JSpecify, atau Kotlin) yang dipahami kompiler Kotlin.
Tetapkan strategi di titik batas: setiap fungsi yang melintasi JVM boundary harus menentukan nullability secara eksplisit. Di kode baru, deklarasikan tipe non-nullable dengan tegas; di kode lama, dokumentasikan perilaku null dan tangani dengan ?. dan ?:.
Kunci migrasi aman adalah incremental: mengubah file satu per satu sambil terus menjalankan test. Mulai dari file yang paling banyak manfaatnya:
Gradle bisa mengompilasi campuran Java dan Kotlin dalam satu modul, jadi tidak perlu modul terpisah. Sebelum migrasi, pastikan test coverage lengkap sebagai jaring pengaman.
IntelliJ IDEA menyediakan konversi otomatis Java ke Kotlin: pilih file, jalankan Convert Java File to Kotlin File, lalu periksa hasilnya. Hasil konversi sering butuh penyesuaian manual untuk memakai idiom Kotlin, tapi ini mempercepat langkah awal:
./gradlew test./gradlew test memvalidasi bahwa migrasi tidak merusak perilaku. Setelah konversi, refactor hasilnya secara manual: ganti !! dengan elvis, manfaatkan data class dan scope functions, dan ganti loop dengan collection operations.
Beberapa praktik yang menjaga migrasi tetap sehat:
Episode 17 menguasai interoperabilitas dan migrasi: memanggil Java dari Kotlin dan sebaliknya, platform types di batas nullability, anotasi @JvmStatic dan @JvmField, serta strategi migrasi incremental dengan test sebagai jaring pengaman.
Inti yang harus dibawa pulang:
@JvmStatic, @JvmOverloads, dan @JvmField mengontrol ekspos ke Java.Di episode 18 selanjutnya kita akan membahas ecosystem extensions — library kotlinx.coroutines, kotlinx.serialization, dan Arrow, pemakaian Kotlin dengan Reactor, Spring, dan Android Jetpack, Kotlin scripting dan aplikasi CLI, serta kontribusi ke ekosistem open-source Kotlin.