Topik Sealed Class pada modul OOP II sudah mempraktikkan permits pada kelas beserta tiga penanda final, sealed, dan non-sealed. Topik ini menuntaskan sisi yang belum disentuh di sana, yaitu penyegelan pada interface, record sebagai penerapan yang alami, dan rantai permits yang bertingkat dari interface ke interface. Himpunan jenis yang tertutup semacam ini menjadi fondasi bagi pattern matching untuk switch pada topik selanjutnya, karena jaminan kelengkapannya berada di tangan kompiler. Pada akhir topik, hierarki yang menggabungkan kelas dan interface dapat dimodelkan tertutup sekaligus tanpa mencatat ulang daftar turunannya di dua tempat.
Sealed interface dan daftar penerapnya#
Interface yang disegel bekerja dengan aturan yang sama seperti kelas yang disegel. Daftar permits menyebut penerap langsung yang diizinkan, dan setiap penerap wajib memilih salah satu penanda. Perbedaannya ada pada kata kunci pemikirannya, karena penerap tidak lagi memakai extends, melainkan implements. Record menjadi penerap yang paling sering dipasangkan dengan sealed interface, karena deklarasi record sudah menyiratkan final sehingga cocok langsung tanpa penanda tambahan.
Program berikut memodelkan himpunan jenis acara sebagai sealed interface, dengan tiga record sebagai penerapnya.
public class UjiJadwal {
public static void main(String[] args) {
Acara[] jadwal = {
new Kuliah("Struktur Data", 90),
new Tugas("Laporan praktikum", "Jumat"),
new Ujian("R. 201", 60)
};
for (Acara a : jadwal) {
System.out.println(ringkas(a));
}
}
static String ringkas(Acara a) {
if (a instanceof Kuliah k) {
return "kuliah " + k.topik() + " (" + k.menit() + " menit)";
} else if (a instanceof Tugas t) {
return "tugas " + t.judul() + ", tenggat " + t.tenggat();
} else if (a instanceof Ujian u) {
return "ujian di " + u.ruangan() + " selama " + u.durasiMenit() + " menit";
}
return "acara tak dikenal";
}
}
sealed interface Acara permits Kuliah, Tugas, Ujian {
}
record Kuliah(String topik, int menit) implements Acara {
}
record Tugas(String judul, String tenggat) implements Acara {
}
record Ujian(String ruangan, int durasiMenit) implements Acara {
}kuliah Struktur Data (90 menit)
tugas Laporan praktikum, tenggat Jumat
ujian di R. 201 selama 60 menitMetode ringkas masih membawa cabang penolong return "acara tak dikenal" di ujungnya, dan itu memang wajar. Pemeriksaan instanceof tidak memiliki jaminan kelengkapan apa pun di mata kompiler, seberapa pun hierarkinya tertutup, sehingga cabang penolong itu tetap dibutuhkan. Daftar jenis yang tertutup baru menghasilkan manfaat penuh ketika pemeriksaannya dipindahkan ke switch, dan itulah yang dibahas pada topik pattern matching untuk switch.
Penolakan kompiler atas penerapan di luar daftar#
Gerbang permits pada interface ditutup saat kompilasi, sama seperti pada kelas. Kelas yang menyatakan implements tanpa masuk daftar ditolak, dan penerapan yang terdaftar tetapi ditulis polos juga ditolak. Pesan penolakannya dapat diuji langsung, dan hasilnya dituliskan pada dua potongan berikut.
// tidak-dijalankan
class Forum implements Acara {
}
// error: class is not allowed to extend sealed class: Acara
// (as it is not listed in its 'permits' clause)Pesan errornya memakai kata extend meskipun pelanggarannya terjadi pada implements, pilihan kata yang diberikan kompiler untuk kedua arah pewarisan. Yang penting dari pesan itu adalah lokasi penunjuknya, yaitu deklarasi penerap yang menyalahi daftar. Perbaikannya jelas: memasukkan kelas tersebut ke permits atau menghapus hubungannya.
// tidak-dijalankan
class Kuliah implements Acara {
}
// error: sealed, non-sealed or final modifiers expectedPenolakan kedua menyasar penanda, bukan daftar. Kuliah sudah masuk permits, tetapi tanpa penanda, kompiler tidak tahu nasib cabangnya, apakah ditutup, disegel lanjut, atau dibuka. Record bebas dari masalah ini karena finalitasnya sudah tertulis dalam deklarasinya, dan itulah alasan pasangan sealed interface dengan record terasa paling mulus.
Menyegel bertingkat dari interface ke interface#
permits tidak berhenti pada satu tingkat. Sealed interface boleh diperluas oleh sealed interface lain, dan setiap interface anak membawa permits miliknya sendiri. Induk hanya mencatat anak-anak langsungnya, sedangkan daun hierarki hanya dicatat oleh induk langsungnya, sehingga daftar pilihan tidak menumpuk di satu tempat. Program berikut memecah Sensor menjadi dua keluarga, analog dan digital, sebelum turun ke penerapnya.
public class UjiSensor {
public static void main(String[] args) {
Sensor[] sensor = {
new SensorSuhu(28.5),
new SensorGerak(true)
};
for (Sensor s : sensor) {
System.out.println(baca(s));
}
}
static String baca(Sensor s) {
if (s instanceof SensorSuhu suhu) {
return "suhu " + suhu.celsius() + " derajat";
} else if (s instanceof SensorGerak gerak) {
return "gerak terdeteksi: " + gerak.menyala();
}
return "sensor tak dikenal";
}
}
sealed interface Sensor permits SensorAnalog, SensorDigital {
}
sealed interface SensorAnalog extends Sensor permits SensorSuhu {
}
sealed interface SensorDigital extends Sensor permits SensorGerak {
}
record SensorSuhu(double celsius) implements SensorAnalog {
}
record SensorGerak(boolean menyala) implements SensorDigital {
}suhu 28.5 derajat
gerak terdeteksi: trueRantai penyegelannya terbaca dari atas ke bawah, yaitu Sensor menutup dua interface keluarganya, dan masing-masing keluarga menutup penerapnya. Keluarga analog berisi sensor suhu, sedangkan keluarga digital berisi sensor gerak, dan tidak ada satu pun jalan untuk menyelipkan penerap baru di luar daftar. Diagram berikut menuntaskan bentuk hierarki itu beserta jembatan antar tingkatnya.
Menimbang penyegelan pada kelas atau pada interface#
Pilihan antara sealed class dan sealed interface berputar pada apa yang dibagi oleh anggota hierarkinya. Bila turunan berbagi field dan perilaku konkret, misalnya kelas dasar yang menyimpan koordinat bersama, sealed class yang lebih tepat, sebagaimana dipraktikkan pada topik Sealed Class. Bila anggota hierarkinya murni jenis data yang berbeda-beda bentuknya, sealed interface dengan record penerap lebih ringkas, karena setiap jenis membawa komponennya sendiri tanpa field warisan yang menuntut konstruktor bertingkat.
Aturan keberadaan yang berlaku juga sama. Tanpa modul bernama, seluruh pihak pada permits wajib berada dalam package yang sama. Bila induk beserta penerapnya berada dalam satu berkas, permits bahkan boleh dilepas. Bagian yang memang dirancang terbuka bagi pihak luar tetap ditulis sebagai interface biasa, karena permits akan menghalangi perluasan yang justru diharapkan. Dengan begitu, penyegelan dipakai pada himpunan jenis yang tertutup secara domain, dan daerah perluasannya tetap terlihat jelas dari deklarasinya.
Kesalahan umum#
- Mengira
permitspada interface menerima penerap yang ditulis polos. Bentuk polos ditolak dengan pesansealed, non-sealed or final modifiers expected. Pilih penandanya sesuai niat cabang, dan record tidak membutuhkannya karena sudah tersirat. - Mencatat seluruh penerap pada induk teratas.
permitshanya menerima anak langsung, sehingga record pada tingkat bawah wajib dicatat oleh interface keluarganya, bukan oleh induknya. - Menunggu pemeriksaan
instanceofmenjadi ekshaustif karena hierarkinya tertutup. Pemeriksaan itu tetap membutuhkan cabang penolong, dan jaminan kelengkapannya baru berlaku pada switch yang dibahas pada topik pattern matching untuk switch. - Menyegel interface yang sebenarnya adalah titik perluasan, misalnya kontrak plugin. Daftar
permitsmengunci siapa saja yang boleh menerapkan, sehingga kontrak yang dibuka bagi pihak luar sebaiknya tetap interface biasa.
Ringkasan#
sealedberlaku pada interface dengan aturan yang sama seperti pada kelas, yaitu daftarpermitsuntuk penerap langsung dan penandafinal,sealed, ataunon-sealedpada masing-masing penerap.- Record menjadi penerap alami karena finalitasnya tersirat, sehingga pasangan sealed interface dengan record memodelkan himpunan jenis tertutup dengan paling sedikit tulisan.
- Penyegelan dapat berantai dari interface ke interface, dan setiap tingkat hanya mencatat anak langsungnya pada
permitsmiliknya. - Pemeriksaan
instanceoftetap membutuhkan cabang penolong seberapa pun hierarkinya tertutup, sedangkan jaminan kelengkapannya baru terpakai penuh oleh switch.
Referensi#
- JEP 409: Sealed Classes — OpenJDK — proposal fitur yang menetapkan aturan penyegelan untuk kelas dan interface sekaligus.
- sealed and non-sealed Interfaces, JLS SE 21 §9.1.1.4 — Oracle — spesifikasi deklarasi interface disegel beserta pilihan penandanya.
- Sealed Classes and Interfaces — Oracle Java Language Updates — panduan resmi fitur pada dokumentasi Java 21.