Enum yang paling sederhana hanya berupa daftar konstanta, tetapi Java mengizinkannya membawa konstruktor, field, dan metode sebagaimana kelas biasa, ditambah dua koleksi khusus bernama EnumSet dan EnumMap yang dirancang untuk kunci bertipe enum. Topik ini melengkapi enum dengan keempat kemampuan itu, lalu menutup dengan pola enum sebagai singleton, yaitu kelas yang objeknya dijamin hanya satu. Dasar-dasarnya berlaku seperti biasa, yaitu konstanta dipisah koma, values() mengembalikan seluruh konstanta menurut urutan deklarasinya, dan ordinal() memberi nomor urut mulai dari nol.
Konstruktor, field, dan metode pada enum#
Konstruktor enum dipanggil otomatis oleh setiap konstanta pada saat kelasnya dimuat, dan argumennya ditulis di dalam tanda kurung setelah nama konstanta. Konstruktor ini boleh diberi parameter tetapi tidak boleh dipanggil dengan new, karena pembuatan objeknya sepenuhnya milik JVM. Field yang dibawanya lazim dinyatakan final agar nilai konstanta tidak berubah, sedangkan metode tambahan memberi perilaku yang mengikuti data konstanta itu sendiri.
public class DemoHari {
public static void main(String[] args) {
for (Hari h : Hari.values()) {
System.out.println(h + " libur? " + h.libur());
}
System.out.println("Lima hari kerja berikutnya setelah Kamis:");
Hari h = Hari.KAMIS;
int dicetak = 0;
while (dicetak < 5) {
h = h.berikutnya();
if (!h.libur()) {
System.out.println(h);
dicetak = dicetak + 1;
}
}
}
}
enum Hari {
SENIN(false),
SELASA(false),
RABU(false),
KAMIS(false),
JUMAT(false),
SABTU(true),
MINGGU(true);
private final boolean akhirPekan;
Hari(boolean akhirPekan) {
this.akhirPekan = akhirPekan;
}
boolean libur() {
return akhirPekan;
}
Hari berikutnya() {
return values()[(ordinal() + 1) % values().length];
}
}SENIN libur? false
SELASA libur? false
RABU libur? false
KAMIS libur? false
JUMAT libur? false
SABTU libur? true
MINGGU libur? true
Lima hari kerja berikutnya setelah Kamis:
JUMAT
SENIN
SELASA
RABU
KAMISSetiap konstanta membawa nilai akhirPekan miliknya sendiri, dan metode libur() membacanya tanpa percabangan di tempat pemakai. Metode berikutnya() menunjukkan sisi lain yang berguna, yaitu enum dapat mengembalikan konstanta lain dari enum yang sama, sehingga perputaran hari cukup dihitung dari ordinal() dan panjang daftarnya.
Metode yang isinya berbeda per konstanta#
Enum juga boleh memuat metode abstrak, dan setiap konstanta mengisinya dengan badan milik sendiri, yaitu blok kurung kurawal yang ditulis tepat setelah nama konstantanya. Pola ini cocok untuk perilaku yang benar-benar berbeda antarkonstanta, karena rumusnya tinggal berada di tempat konstanta itu dideklarasikan. Contoh kalkulator berikut membagi tiga operasi aritmetika ke dalam satu enum.
public class DemoOperasi {
public static void main(String[] args) {
for (Operasi o : Operasi.values()) {
System.out.println(o + ": " + o.hitung(10, 4));
}
}
}
enum Operasi {
TAMBAH {
double hitung(double a, double b) {
return a + b;
}
},
KURANG {
double hitung(double a, double b) {
return a - b;
}
},
KALI {
double hitung(double a, double b) {
return a * b;
}
};
abstract double hitung(double a, double b);
}TAMBAH: 14.0
KURANG: 6.0
KALI: 40.0Metode hitung dinyatakan abstract setelah daftar konstanta, sehingga setiap konstanta wajib menyediakan badannya, dan kelalaian satu konstanta pun menghentikan kompilasi. Perhatikan urutan penulisannya, yaitu konstanta beserta badannya didahulukan, tanda titik koma menutup daftar konstanta, lalu anggota lain menyusul. Enum yang badan konstantanya berbeda-beda sebenarnya diwujudkan JVM sebagai kelas turunan anonim per konstanta, sehingga perilakunya tetap dikerjakan dynamic dispatch.
EnumSet dan EnumMap#
EnumSet adalah kumpulan konstanta enum yang diwujudkan sebagai bit vector, yakni bilangan panjang yang setiap bitnya mewakili satu konstanta, sehingga himpunan semacam ini jauh lebih ringkas daripada HashSet. EnumMap adalah peta yang kuncinya enum, dan isinya disimpan pada array yang indeksnya dihitung dari ordinal(), sehingga pencariannya lebih cepat daripada HashMap tanpa hashing sama sekali. Keduanya mengiterasi isinya menurut urutan deklarasi enum, bukan menurut urutan penyisipan.
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
public class DemoJadwal {
public static void main(String[] args) {
EnumSet<Hari> hariKerja = EnumSet.range(Hari.SENIN, Hari.JUMAT);
System.out.println("Hari kerja: " + hariKerja.size() + " hari");
EnumMap<Hari, String> kegiatan = new EnumMap<>(Hari.class);
kegiatan.put(Hari.SENIN, "Rapat pekanan");
kegiatan.put(Hari.SELASA, "Olahraga");
kegiatan.put(Hari.SABTU, "Berkebun");
for (Map.Entry<Hari, String> entri : kegiatan.entrySet()) {
System.out.println(entri.getKey() + ": " + entri.getValue());
}
System.out.println("Jadwal Kamis: " + kegiatan.getOrDefault(Hari.KAMIS, "tidak ada"));
}
}
enum Hari {
SENIN, SELASA, RABU, KAMIS, JUMAT, SABTU, MINGGU
}Hari kerja: 5 hari
SENIN: Rapat pekanan
SELASA: Olahraga
SABTU: Berkebun
Jadwal Kamis: tidak adaMetode pabrik EnumSet.range mengambil seluruh konstanta di antara dua batasnya, sedangkan EnumSet.of menghimpun konstanta yang disebutkan satu per satu. Peta kegiatan dicetak menurut urutan deklarasi Hari, yaitu Senin lebih dulu meskipun penyisipannya tidak berurutan. Bila jenis enum-nya bertambah, kedua koleksi ini ikut menyesuaikan tanpa perubahan kode pemakai.
Enum sebagai singleton#
Singleton adalah pola yang menjamin sebuah kelas hanya punya satu objek sepanjang hidup program, dan enum dengan satu konstanta mencapainya tanpa satu baris penjagaan pun. JVM membuat konstanta enum tepat satu kali saat kelasnya dimuat, new dilarang pada tipe enum, dan mekanisme refleksi menolak membuat objek enum baru. Jaminan satuannya terjaga dari semua arah yang biasa merusak singleton buatan sendiri. Serialisasi enum juga dikendalikan JVM berdasarkan nama konstantanya, bukan berdasarkan salinan keadaannya, sehingga objek yang dibaca kembali tetap menunjuk konstanta yang sama.
import java.util.HashMap;
import java.util.Map;
public class UjiSingleton {
public static void main(String[] args) {
PenyimpanPengaturan a = PenyimpanPengaturan.INSTANS;
PenyimpanPengaturan b = PenyimpanPengaturan.INSTANS;
a.simpan("tema", "gelap");
System.out.println("Tema menurut b: " + b.baca("tema"));
System.out.println("a == b: " + (a == b));
}
}
enum PenyimpanPengaturan {
INSTANS;
private final Map<String, String> data = new HashMap<>();
void simpan(String kunci, String nilai) {
data.put(kunci, nilai);
}
String baca(String kunci) {
return data.get(kunci);
}
}Tema menurut b: gelap
a == b: trueKedua variabel memegang konstanta yang sama persis, sehingga data yang disimpan lewat a langsung terbaca lewat b, dan perbandingan == menegaskannya. Bandingkan dengan singleton yang menulis sendiri logika penjagaannya, yang tetap harus berhati-hati terhadap refleksi dan serialisasi, sedangkan versi enum menyerahkan soal pembuatan objek, refleksi, dan serialisasi kepada JVM.
Kesalahan umum#
- Memanggil
newpada tipe enum. Konstanta enum dibuat JVM, sehingganew Hari(...)ditolak kompiler dengan pesanenum types may not be instantiated. Tambahkan konstanta baru pada deklarasinya bila nilai lain dibutuhkan. - Menyimpan
ordinal()sebagai data yang bertahan lama. Nomor urut berubah setiap kali konstanta baru disisipkan, sehingga data yang memakainya sebagai kunci ikut kacau. Simpanname()atau field milik enum sendiri sebagai penandanya. - Menyisakan badan kosong pada konstanta yang mewarisi metode abstrak. Konstanta tanpa badan untuk metode abstrak enum ditolak kompiler dengan pesan
is not abstract and does not override abstract method. Isi badan konstantanya, atau jadikan metodenya konkret di tingkat enum. - Membandingkan konstanta dengan
equalstanpa menyiapkannull. Kedua cara benar untuk konstanta yang tidaknull, tetapi==tetap aman bila variabelnyanull, sedangkanequalsmelemparNullPointerException. Gunakan==sebagai kebiasaan bawaan untuk konstanta enum.
Ringkasan#
- Enum dapat membawa konstruktor, field
final, dan metode, dan setiap konstanta dibuat tepat satu kali oleh JVM saat kelasnya dimuat. - Badan per konstanta mengisi metode abstrak milik enum, sehingga perilaku yang berbeda antarkonstanta tinggal berada di tempat konstantanya dideklarasikan.
EnumSetdanEnumMapmemakai representasi berbasisordinal()yang ringkas dan cepat, dan keduanya mengiterasi menurut urutan deklarasi enum.- Enum dengan satu konstanta menjadi singleton yang dijamin JVM, karena
new, refleksi, dan serialisasi sama-sama tidak menghasilkan konstanta baru.
Referensi#
- The Java Tutorials: Enum Types — Oracle — Deklarasi enum, konstruktornya, metode, dan badan per konstanta.
- EnumSet (Java SE 21 & JDK 21) — Oracle — Dokumentasi representasi himpunan enum beserta metode pabriknya.
- EnumMap (Java SE 21 & JDK 21) — Oracle — Dokumentasi peta berkunci enum dan urutan iterasinya.