Menjembatani ekosistem Java dan Scala: memakai library Java secara langsung, konversi collection dengan scala.jdk.CollectionConverters, menulis class Java yang dipakai Scala, serta strategi migrasi dari Scala 2 ke 3 dan dari aplikasi Java ke Scala secara bertahap.

Sejak episode 2 kita tahu satu fakta fundamental: Scala berjalan di JVM dan dikompilasi menjadi bytecode yang sama dengan Java. Episode ini mengubah fakta itu menjadi keunggulan praktis — kalian bisa memakai hampir seluruh library Java yang ada di dunia, dan menulis kode yang bisa dipanggil dari Java.
Mengapa episode ini penting? Karena interop adalah salah satu alasan terbesar perusahaan memilih Scala: tidak perlu meninggalkan investasi Java. Kalian bisa memakai java.time, library Java populer, dan framework Java langsung dari kode Scala — dan sebaliknya, tim Java bisa memakai library Scala tanpa menulis Scala.
Interop Java → Scala berjalan tanpa biaya. Contoh paling umum: java.time untuk tanggal:
import java.time.{LocalDate, Period}
val today = LocalDate.now()
val deadline = today.plusDays(30)
println(s"Deadline: $deadline")
val age = Period.between(LocalDate.of(1990, 1, 1), today).getYearsKarena Scala memperlakukan semuanya sebagai objek, API Java terasa natural: method chaining, nullability yang bisa di-Option-kan, dan exception yang bisa di-Try-kan:
import scala.util.Try
import java.net.URI
def isValidUrl(raw: String): Boolean =
Try(URI(raw).toURL).isSuccessLibrary Java bisa mengembalikan null; Scala tidak mengenalnya. Konversi selalu lewat Option:
import java.util.Map as JMap
val javaMap: JMap[String, String] = javaLibrary.getConfig()
val value: Option[String] = Option(javaMap.get("port"))Option(javaMap.get("port")) — jika Java mengembalikan null, hasilnya None; jika string, Some(...). Tidak ada lagi NPE yang lolos ke kode Scala.
Java dan Scala punya collection yang berbeda (List/Map/Set). Konversi satu arah tidak otomatis — gunakan scala.jdk.CollectionConverters:
import scala.jdk.CollectionConverters.*
val scalaList = List("a", "b", "c")
val javaList: java.util.List[String] = scalaList.asJava
val javaSet: java.util.Set[Int] = java.util.Set.of(1, 2, 3)
val scalaSet: Set[Int] = javaSet.asScala.toSet
val javaMap: java.util.Map[String, Int] = java.util.Map.of("x", 1)
val scalaMap: Map[String, Int] = javaMap.asScala.toMapBiasakan konversi di perbatasan: dalam kode Scala, bekerja dengan collection Scala; konversi hanya saat memanggil API Java dan saat menerima hasilnya. Ini menjaga idiom bahasa tetap konsisten dan menghindari konversi berulang.
Note
asJava untuk collection Scala yang immutable menghasilkan view Java — mutasi lewat view akan melempar exception, sesuai prinsip immutability. Jika benar-benar butuh collection Java yang mutable, gunakan List[String]().asJava.asInstanceOf[java.util.List[String]] atau buat mutable copy.
Kelas Java biasa dipakai seperti kelas Scala — dengan perbedaan kecil pada constructor:
import java.util.ArrayList
val list = new ArrayList[String]() // butuh new untuk class Java
list.add("Scala")Perhatikan: class Java selalu butuh new (tidak ada companion apply), sementara case class Scala tidak.
Sebaliknya, kode Scala bisa dipanggil dari Java tanpa pemanggil menulis Scala:
package com.example
object Greeter:
def greet(name: String): String = s"Hello, $name!"package com.example;
public class Main {
public static void main(String[] args) {
System.out.println(Greeter.greet("Java"));
}
}Karena object Greeter dikompilasi menjadi class Java dengan method statis, pemanggilan dari Java tidak berbeda dengan memanggil utility class biasa. Interop dua arah ini membuat Scala bisa diadopsi bertahap di dalam codebase Java.
Kode Scala 2 yang sudah jalan bisa diupgrade dengan alat otomatis:
implicit val → given).Langkah praktisnya:
1. Nyalakan flag -source:3.0-migration pada Scala 2.13
2. Perbaiki warning hingga bersih
3. Pindahkan scalaVersion ke 3.3.x (LTS)
4. Jalankan scalafix untuk migrasi sintaks
5. Perbaiki sisa error secara manualTidak semua kode 2.13 bermigrasi otomatis — terutama yang bergantung pada detail compiler lama — tetapi untuk mayoritas codebase, jalur migrasi ini terukur dan bertahap.
Adopsi Scala di codebase Java paling aman dilakukan bertahap, mengikuti prinsip:
Strategi yang umum:
Keuntungan pendekatan ini: risiko rendah, nilai terlihat cepat (modul pertama), dan tim tidak perlu "big bang" yang membahayakan produksi.
java.util.List merambat.Option — String.valueOf-style API bisa memberi null; selalu bungkus.new yang terlupa untuk class Java — Scala tidak butuh new untuk case class, tetapi class Java selalu memerlukannya.Inti yang harus dibawa pulang:
java.time) langsung dipakai dari Scala tanpa adapter.Option(...) membungkus null Java; CollectionConverters (.asJava/.asScala) mengonversi collection di perbatasan.Di episode 19 selanjutnya, kita menguji lebih dalam: testing lanjut dan property-based — ScalaCheck forAll, law-based testing, mocking, dan testcontainers untuk integrasi nyata. Sampai jumpa di episode 19!