Sealed class dan interface menjadi fitur final di JDK 17 lewat JEP 409, sementara pattern matching untuk switch menjadi final di JDK 21 lewat JEP 441. Keduanya melewati lima JEP bertahap: JEP 406 di JDK 17, JEP 420 di JDK 18, JEP 427 di JDK 19, JEP 433 di JDK 20, lalu JEP 441. Efek gabungannya, compiler bisa tahu semua kemungkinan subtype dan menolak switch yang belum menangani semuanya.
TL;DR
- Sealed membatasi kelas atau interface mana yang boleh menjadi turunan, dan daftarnya ditulis di deklarasi.
- Pattern switch di JDK 21 mewajibkan switch statement dengan pola menutup semua nilai yang mungkin.
- Switch lama tetap kompilasi tanpa perubahan dan berjalan dengan semantik identik.
- Dua penyempurnaan di versi final: pola dalam tanda kurung dihapus, konstanta enum berkualifikasi diizinkan.
Apa itu sealed class dan kenapa dipakai?
Sealed class dan sealed interface membatasi kelas atau interface lain mana yang boleh memperluas atau mengimplementasikannya. Batas itu ditulis langsung di deklarasi lewat klausa permits, sehingga hierarki tipe menjadi tertutup dan bisa diketahui compiler. JEP 409 memfinalkan fitur ini di JDK 17 tanpa perubahan dari JDK 16, dan menegaskan bahwa tujuan fitur ini bukan mengubah perilaku kata kunci final.
Sebelum ada sealed, ada dua pilihan ekstrem yang sama-sama tidak nyaman. Membuat kelas final berarti nol subclass, sehingga tidak bisa diperluas sama sekali. Membiarkannya terbuka berarti siapa pun bisa menambah subclass baru, dan compiler tidak akan pernah bisa memastikan daftar turunan sudah lengkap. Sealed menawarkan jalan tengah: hierarki tetap terkontrol, tapi masih bisa punya beberapa implementasi.
public sealed interface Celestial permits Planet, Star, Comet {
double mass();
}
final class Planet implements Celestial {
public double mass() { return 0; }
}
final class Star implements Celestial {
public double mass() { return 0; }
}
final class Comet implements Celestial {
public double mass() { return 0; }
}
Perhatikan bahwa tiap implementasi di atas ditandai final. Itu bukan keharusan mutlak, karena turunan sealed juga bisa sealed atau non-sealed, tapi menandainya final membuat hierarki benar-benar tertutup dan mudah dianalisis. Untuk model domain yang jumlah variannya sudah pasti, pola ini membuat kode lebih mudah dibaca daripada hierarki terbuka yang harus dirawat dengan dokumentasi.
Kapan pattern matching switch diwajibkan exhaustive?
Switch statement yang memakai pola wajib menutup semua nilai yang mungkin, dan itu berlaku sejak JDK 21. JEP 441 menyebut salah satu tujuannya adalah meningkatkan keamanan switch statement dengan mewajibkan cakupan penuh atas semua nilai input. Switch expression sudah lebih dulu menuntut hal serupa, jadi yang berubah adalah kewajiban itu kini berlaku juga untuk statement.
Kewajiban ini masuk akal ketika dikombinasikan dengan sealed. Kalau hierarki tipe tertutup dan daftar turunannya diketahui compiler, maka daftar nilai yang mungkin juga diketahui. Compiler bisa memeriksa apakah setiap varian sudah punya cabang, dan menolak kode yang melewatkan salah satunya. Tanpa sealed, pemeriksaan itu tidak mungkin dilakukan karena selalu ada kemungkinan subclass baru di masa depan.
static String describe(Celestial c) {
return switch (c) {
case Planet p -> "planet dengan massa " + p.mass();
case Star s -> "bintang dengan massa " + s.mass();
case Comet k -> "komet dengan massa " + k.mass();
};
}
Kode di atas tidak memerlukan cabang default, karena ketiga varian sudah tercakup dan tidak ada varian lain yang mungkin. Kalau nanti ada yang menambahkan implementasi baru ke interface Celestial, deklarasi permits harus diubah, dan di titik itulah compiler akan menandai switch yang belum diperbarui. Inilah manfaat praktisnya: perubahan hierarki memunculkan error kompilasi di tempat yang tepat, bukan bug runtime yang muncul belakangan.
Pola yang sama berlaku untuk record pattern, yang berkembang bersama fitur ini lewat JEP 440. Ketika sebuah record bisa didekomposisi langsung di case, cabang switch menjadi lebih ringkas karena kamu tidak perlu memanggil accessor satu per satu. Keduanya dirancang agar bisa dipakai bersamaan, bukan sebagai fitur yang saling menggantikan.
Apa yang berubah dari versi preview ke final?
Dua perubahan substantif terjadi di versi final: pola dalam tanda kurung dihapus karena dinilai tidak memberi nilai tambah, dan konstanta enum berkualifikasi diizinkan sebagai konstanta case. Selebihnya perubahan editorial. Riwayat lengkapnya penting karena banyak tutorial di internet masih memakai sintaks preview dari JDK 17 sampai 20, yang sebagian sudah tidak berlaku di JDK 21.
| Tahap | JEP | Versi | Status |
|---|---|---|---|
| Proposal awal | JEP 406 | JDK 17 | Preview |
| Penyempurnaan | JEP 420 | JDK 18 | Preview kedua |
| Penyempurnaan | JEP 427 | JDK 19 | Preview ketiga |
| Penyempurnaan | JEP 433 | JDK 20 | Preview keempat |
| Final | JEP 441 | JDK 21 | Delivered |
Tanggal pencatatan resmi kedua JEP juga bisa jadi penanda kematangan fitur. JEP 409 dibuat pada 27 Januari 2021 dan baru dinyatakan delivered di JDK 17, sementara JEP 441 dibuat pada 18 Januari 2023 dan delivered di JDK 21. Jarak antara proposal dan finalisasi di kedua kasus menunjukkan bahwa fitur bahasa di Java melewati siklus preview bertahun-tahun, bukan rilis mendadak.
Untuk sealed class, riwayatnya lebih pendek: JEP 360 sebagai preview pertama, JEP 397 sebagai preview kedua, lalu JEP 409 yang memfinalkannya di JDK 17. JEP 409 menegaskan tidak ada perubahan dari JDK 16, jadi kode yang sudah memakai sealed di JDK 16 bisa dipindahkan tanpa penyesuaian sintaks.
Praktisnya, kalau kamu mengikuti tutorial yang menulis pola dengan tanda kurung tambahan, kode itu kemungkinan besar tidak akan kompilasi di JDK 21. Ganti dengan bentuk tanpa tanda kurung, lalu periksa apakah ada konstanta enum yang ditulis tanpa nama kelas, karena bentuk berkualifikasi kini justru yang didukung secara eksplisit.
Bagaimana null dan enum diperlakukan di switch pola?
JEP 441 menargetkan agar sifat historis switch yang menolak null bisa dilonggarkan saat diperlukan. Artinya, kamu bisa menangani null sebagai salah satu cabang secara eksplisit, bukan lagi bergantung pada exception yang dilempar switch konvensional. Ini berguna ketika null adalah kondisi yang sah dan perlu respons berbeda, bukan kesalahan pemrograman.
Untuk enum, versi final mengizinkan konstanta enum berkualifikasi sebagai konstanta case pada switch expression maupun statement. Perubahan ini kecil tapi mengurangi kejutan, terutama pada kode yang sudah memakai nama enum berkualifikasi di tempat lain dan ingin konsisten di dalam switch.
Yang tidak berubah adalah perilaku switch lama. JEP 441 secara eksplisit menetapkan bahwa semua switch expression dan statement yang sudah ada tetap kompilasi tanpa perubahan dan berjalan dengan semantik identik. Jadi menaikkan versi JDK tidak akan diam-diam mengubah arti kode yang sudah kamu tulis. Kalau ada yang rusak setelah upgrade, penyebabnya bukan fitur ini.
Bagaimana menerapkannya di kode yang sudah ada?
Terapkan bertahap: mulai dari hierarki tipe yang variannya sudah pasti, ubah menjadi sealed, lalu ganti rangkaian if-else atau instanceof berantai dengan switch pola. Jangan mengubah semua sekaligus, karena manfaat utama fitur ini baru terasa ketika hierarki benar-benar tertutup dan compiler bisa memeriksa kelengkapan cabang.
- Pilih satu hierarki tipe yang jumlah implementasinya stabil dan tidak akan bertambah tanpa keputusan sadar.
- Tambahkan sealed pada interface atau kelas dasarnya, lalu daftarkan implementasi yang ada di klausa permits.
- Tandai tiap implementasi sebagai final, sealed, atau non-sealed sesuai kebutuhan desain.
- Ganti percabangan berbasis instanceof dengan switch pola, dan hapus cabang default yang sudah tidak diperlukan.
- Kompilasi dan pastikan tidak ada error exhaustiveness. Error di sini adalah temuan yang berguna, bukan gangguan.
Langkah kelima adalah inti manfaatnya. Setiap kali hierarki berubah, compiler memberi tahu semua tempat yang perlu diperbarui. Itu menggantikan pola lama di mana penambahan varian baru hanya memunculkan bug di runtime, biasanya berupa cabang yang tidak pernah dieksekusi atau exception yang tidak tertangani.
Untuk referensi resmi, baca JEP 441 tentang Pattern Matching for switch, JEP 409 tentang Sealed Classes, dan JEP 440 tentang Record Patterns. Spesifikasi bahasa resminya ada di Java Language Specification edisi 21, dan materi pengantar tersedia di dev.java.
Satu catatan soal migrasi: jangan menjadikan semua hierarki sebagai sealed. Fitur ini paling berguna pada tipe yang memang mewakili sekumpulan varian tertutup, seperti hasil operasi, status pesanan, atau bentuk geometri. Untuk kelas yang memang dirancang untuk diperluas pihak lain, sealed justru menghalangi penggunaan yang sah. Pilih hierarki yang variannya kamu kendalikan sendiri.
Apakah sealed class bisa dipakai tanpa pattern matching?
Bisa, dan itu tetap berguna. Sealed sudah final sejak JDK 17, empat tahun sebelum pattern switch final di JDK 21. Tanpa pattern matching, sealed tetap memberi keuntungan desain: hierarki tipe menjadi eksplisit, dan penambahan implementasi baru harus lewat perubahan deklarasi, bukan sekadar menambah kelas di tempat lain.
Kombinasi keduanya baru memberi pemeriksaan kelengkapan otomatis. Kalau kamu memakai JDK 17 atau 20, sealed sudah tersedia tapi switch pola masih berstatus preview, sehingga perlu flag khusus saat kompilasi. Untuk kode produksi, tunggu JDK 21 agar tidak bergantung pada fitur preview yang sintaksnya masih bisa berubah.
FAQ
Apakah sealed class sama dengan final?
Tidak sama. Final berarti nol subclass, sedangkan sealed berarti subclass dibatasi pada daftar yang disebut di deklarasi. Sealed memberi jalan tengah antara hierarki terbuka dan kelas yang sama sekali tidak bisa diperluas, dan JEP 409 menegaskan bahwa fitur ini tidak mengubah perilaku final.
Apakah switch lama saya perlu ditambah default setelah upgrade ke JDK 21?
Tidak. JEP 441 menetapkan bahwa semua switch expression dan statement yang sudah ada tetap kompilasi tanpa perubahan dan berjalan dengan semantik identik. Kewajiban cakupan penuh hanya berlaku untuk switch yang memakai pola, bukan switch konvensional dengan konstanta.
Apakah pola dalam tanda kurung masih bisa dipakai di JDK 21?
Tidak. Salah satu perubahan di versi final adalah penghapusan pola dalam tanda kurung karena dinilai tidak memberi nilai tambah. Kode dari tutorial lama yang masih memakai bentuk itu perlu disesuaikan dengan menghapus tanda kurung tambahan sebelum bisa dikompilasi di JDK 21.
Apakah pattern matching switch butuh flag preview di JDK 21?
Tidak butuh. Fitur ini sudah final di JDK 21 lewat JEP 441, jadi bisa dipakai tanpa flag preview. Flag preview hanya diperlukan pada JDK 17 sampai 20, ketika fitur ini masih berstatus preview dengan sintaks yang belum stabil.
💬 Komentar (0)
Belum ada komentar. Jadilah yang pertama! 💬