Belajar Scala - Interop dengan Java
Episode 18 of 23

Belajar Scala - Interop dengan Java

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.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

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.

Memakai Library Java dari Scala

Interop Java → Scala berjalan tanpa biaya. Contoh paling umum: java.time untuk tanggal:

Library Java langsung dari Scala
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).getYears

Karena Scala memperlakukan semuanya sebagai objek, API Java terasa natural: method chaining, nullability yang bisa di-Option-kan, dan exception yang bisa di-Try-kan:

API Java yang bisa gagal -> Try
import scala.util.Try
import java.net.URI
 
def isValidUrl(raw: String): Boolean =
  Try(URI(raw).toURL).isSuccess

Null Java vs Scala

Library Java bisa mengembalikan null; Scala tidak mengenalnya. Konversi selalu lewat Option:

Option membungkus null Java
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.

Konversi Collection: CollectionConverters

Java dan Scala punya collection yang berbeda (List/Map/Set). Konversi satu arah tidak otomatis — gunakan scala.jdk.CollectionConverters:

Konversi collection dua arah
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.toMap

Biasakan 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.

Java Class di Scala dan Sebaliknya

Class Java di Scala

Kelas Java biasa dipakai seperti kelas Scala — dengan perbedaan kecil pada constructor:

Memakai class Java
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.

Scala di Java: Tanpa Scala dari Pemanggil

Sebaliknya, kode Scala bisa dipanggil dari Java tanpa pemanggil menulis Scala:

Objek Scala untuk dipakai Java
package com.example
 
object Greeter:
  def greet(name: String): String = s"Hello, $name!"
JavaPanggil dari Java
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.

Migrasi Scala 2 → Scala 3

Kode Scala 2 yang sudah jalan bisa diupgrade dengan alat otomatis:

  • scalafix — tool linting dan rewrite otomatis untuk perubahan sintaks (misal implicit valgiven).
  • TASTy — format tipe (episode 2) memungkinkan Scala 3 membaca library Scala 2 yang sudah di-cross-build.

Langkah praktisnya:

Langkah migrasi 2 -> 3
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 manual

Tidak semua kode 2.13 bermigrasi otomatis — terutama yang bergantung pada detail compiler lama — tetapi untuk mayoritas codebase, jalur migrasi ini terukur dan bertahap.

Migrasi Aplikasi Java → Scala Bertahap

Adopsi Scala di codebase Java paling aman dilakukan bertahap, mengikuti prinsip:

100%

Strategi yang umum:

  1. Mulai dari unit kecil — modul baru (misal utility, parser) ditulis Scala.
  2. Hidup berdampingan — Java dan Scala di-compile dalam satu proyek sbt; keduanya saling memanggil.
  3. Konversi bertahap — pindahkan unit yang paling banyak berubah; tulis test dulu sebagai pengaman.
  4. Pertahankan batas bersih — jaga konversi collection dan null hanya di lapisan antarmuka.

Keuntungan pendekatan ini: risiko rendah, nilai terlihat cepat (modul pertama), dan tim tidak perlu "big bang" yang membahayakan produksi.

Common Pitfalls

  • Collection Java di tengah kode Scala — segera konversi di perbatasan; jangan biarkan java.util.List merambat.
  • Null Java tanpa OptionString.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.
  • Kelas Scala di Java dengan default arguments — default arguments Scala tidak terlihat dari Java; pemanggil harus memberikan semua argumen.

Penutup

Inti yang harus dibawa pulang:

  • Library Java (mis. java.time) langsung dipakai dari Scala tanpa adapter.
  • Option(...) membungkus null Java; CollectionConverters (.asJava/.asScala) mengonversi collection di perbatasan.
  • Class Scala bisa dipanggil dari Java (object → method statis) dan sebaliknya.
  • Migrasi Scala 2 → 3 memakai scalafix + TASTy; migrasi Java → Scala paling aman bertahap per modul.

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!

Belajar Scala - Interop dengan Java | Belajar Scala