Melampirkan Nilai ke Java Enum

1. Perkenalan

Jenis enum Java menyediakan cara yang didukung bahasa untuk membuat dan menggunakan nilai konstan. Dengan menentukan sekumpulan nilai yang terbatas, enum lebih aman untuk tipe daripada variabel literal konstan seperti String atau int .

Namun, nilai enum diperlukan untuk menjadi pengenal yang valid , dan kami didorong untuk menggunakan SCREAMING_SNAKE_CASE berdasarkan konvensi.

Dengan batasan tersebut, nilai enum saja tidak cocok untuk string yang dapat dibaca manusia atau nilai non-string .

Dalam tutorial ini, kita akan menggunakan fitur enum sebagai kelas Java untuk melampirkan nilai yang kita inginkan.

2. Menggunakan Java Enum sebagai Kelas

Kami sering membuat enum sebagai daftar nilai sederhana. Misalnya, berikut adalah dua baris pertama dari tabel periodik sebagai enum sederhana :

public enum Element { H, HE, LI, BE, B, C, N, O, F, NE }

Menggunakan sintaks di atas, kita telah membuat sepuluh contoh statis terakhir dari enum bernama Element . Meskipun ini sangat efisien, kami hanya menangkap simbol elemen. Dan sementara bentuk huruf besar sesuai untuk konstanta Java, itu bukan cara kita biasanya menulis simbol.

Selain itu, kami juga kehilangan properti lain dari elemen tabel periodik, seperti nama dan berat atom.

Meskipun tipe enum memiliki perilaku khusus di Java, kita bisa menambahkan konstruktor, bidang, dan metode seperti yang kita lakukan dengan kelas lain. Karena itu, kita dapat meningkatkan enum kita untuk memasukkan nilai-nilai yang kita butuhkan.

3. Menambahkan Konstruktor dan Bidang Akhir

Mari kita mulai dengan menambahkan nama elemen. Kami akan menetapkan nama menjadi variabel terakhir menggunakan konstruktor :

public enum Element { H("Hydrogen"), HE("Helium"), // ... NE("Neon"); public final String label; private Element(String label) { this.label = label; } }

Pertama-tama, kami memperhatikan sintaks khusus dalam daftar deklarasi. Ini adalah bagaimana konstruktor dipanggil untuk tipe enum . Meskipun ilegal menggunakan operator new untuk enum , kita bisa meneruskan argumen konstruktor dalam daftar deklarasi.

Kami kemudian mendeklarasikan label variabel instan . Ada beberapa hal yang perlu diperhatikan tentang itu.

Pertama, kami memilih pengenal label alih-alih namanya . Meskipun nama field anggota tersedia untuk digunakan, mari kita pilih label untuk menghindari kebingungan dengan metode Enum.name () yang telah ditentukan .

Kedua, bidang label kami adalah final . Meskipun bidang enum tidak harus final , dalam sebagian besar kasus kami tidak ingin label kami berubah. Dalam semangat nilai enum yang konstan, ini masuk akal.

Terakhir, bidang label bersifat publik. Karenanya, kita dapat mengakses label secara langsung:

System.out.println(BE.label);

Di sisi lain, field bisa menjadi private , diakses dengan metode getLabel () . Agar singkat, artikel ini akan terus menggunakan gaya bidang publik.

4. Menemukan Nilai-Nilai Java Enum

Java menyediakan metode valueOf (String) untuk semua jenis enum . Jadi, kita selalu bisa mendapatkan nilai enum berdasarkan nama yang dideklarasikan:

assertSame(Element.LI, Element.valueOf("LI"));

Namun, kami mungkin ingin mencari nilai enum dengan bidang label kami juga. Untuk melakukan itu kita dapat menambahkan metode statis :

public static Element valueOfLabel(String label) { for (Element e : values()) { if (e.label.equals(label)) { return e; } } return null; }

Metode static valueOfLabel () mengulangi nilai Elemen hingga menemukan kecocokan. Ia mengembalikan null jika tidak ada kecocokan yang ditemukan. Sebaliknya, pengecualian bisa dilemparkan alih-alih mengembalikan nol .

Mari kita lihat contoh cepat menggunakan metode valueOfLabel () kita :

assertSame(Element.LI, Element.valueOfLabel("Lithium"));

5. Menyimpan Nilai Pencarian

Kita bisa menghindari iterasi nilai enum dengan menggunakan Map untuk menyimpan label ke dalam cache . Untuk melakukan ini, kami mendefinisikan Peta akhir statis dan mengisinya ketika kelas memuat:

public enum Element { // ... enum values private static final Map BY_LABEL = new HashMap(); static { for (Element e: values()) { BY_LABEL.put(e.label, e); } } // ... fields, constructor, methods public static Element valueOfLabel(String label) { return BY_LABEL.get(label); } }

Sebagai hasil dari cache, nilai enum hanya diiterasi sekali , dan metode valueOfLabel () disederhanakan.

Sebagai alternatif, kita bisa dengan malas membuat cache saat pertama kali diakses dalam metode valueOfLabel () . Dalam hal ini, akses peta harus disinkronkan untuk mencegah masalah konkurensi.

6. Melampirkan Banyak Nilai

The Enum konstruktor dapat menerima beberapa nilai . Untuk mengilustrasikan, mari tambahkan nomor atom sebagai int dan berat atom sebagai pelampung :

public enum Element { H("Hydrogen", 1, 1.008f), HE("Helium", 2, 4.0026f), // ... NE("Neon", 10, 20.180f); private static final Map BY_LABEL = new HashMap(); private static final Map BY_ATOMIC_NUMBER = new HashMap(); private static final Map BY_ATOMIC_WEIGHT = new HashMap(); static { for (Element e : values()) { BY_LABEL.put(e.label, e); BY_ATOMIC_NUMBER.put(e.atomicNumber, e); BY_ATOMIC_WEIGHT.put(e.atomicWeight, e); } } public final String label; public final int atomicNumber; public final float atomicWeight; private Element(String label, int atomicNumber, float atomicWeight) { this.label = label; this.atomicNumber = atomicNumber; this.atomicWeight = atomicWeight; } public static Element valueOfLabel(String label) { return BY_LABEL.get(label); } public static Element valueOfAtomicNumber(int number) { return BY_ATOMIC_NUMBER.get(number); } public static Element valueOfAtomicWeight(float weight) { return BY_ATOMIC_WEIGHT.get(weight); } }

Demikian pula, kita dapat menambahkan nilai apa pun yang kita inginkan ke enum , seperti simbol huruf yang tepat, "He", "Li", dan "Be", misalnya.

Selain itu, kita dapat menambahkan nilai yang dihitung ke enum kita dengan menambahkan metode untuk melakukan operasi.

7. Mengontrol Antarmuka

Sebagai hasil dari penambahan kolom dan metode ke enum kami , kami telah mengubah antarmuka publiknya. Oleh karena itu, kode kami, yang menggunakan metode inti nama Enum () dan nilaiOf () , tidak akan mengetahui bidang baru kami.

Metode valueOf () statis sudah ditentukan untuk kita oleh bahasa Java. Oleh karena itu, kami tidak dapat memberikan implementasi valueOf () kami sendiri .

Demikian pula, karena metode Enum.name () adalah final, kami juga tidak dapat menimpanya .

Akibatnya, tidak ada cara praktis untuk memanfaatkan bidang ekstra kami menggunakan API Enum standar . Sebaliknya, mari kita lihat beberapa cara berbeda untuk mengekspos bidang kita.

7.1. Mengganti toString ()

Mengganti toString () mungkin merupakan alternatif untuk mengganti nama () :

@Override public String toString() { return this.label; }

Secara default, Enum.toString () mengembalikan nilai yang sama dengan Enum.name ().

7.2. Menerapkan Antarmuka

The enum jenis di Jawa dapat mengimplementasikan antarmuka . Meskipun pendekatan ini tidak generik seperti Enum API, antarmuka membantu kami menggeneralisasi.

Mari pertimbangkan antarmuka ini:

public interface Labeled { String label(); }

Untuk konsistensi dengan metode Enum.name () , metode label () kita tidak memiliki awalan get .

Dan, karena metode valueOfLabel () bersifat statis , kami tidak memasukkannya ke antarmuka kami.

Akhirnya, kita bisa mengimplementasikan antarmuka di enum kita :

public enum Element implements Labeled { // ... @Override public String label() { return label; } // ... }

Salah satu keuntungan dari pendekatan ini adalah bahwa antarmuka Berlabel dapat diterapkan ke semua kelas, tidak hanya tipe enum . Alih-alih mengandalkan API Enum generik , kami sekarang memiliki API yang lebih spesifik konteks.

8. Kesimpulan

Dalam artikel ini, kami telah menjelajahi banyak fitur implementasi Java Enum . Dengan menambahkan konstruktor, bidang, dan metode, kita melihat bahwa enum dapat melakukan lebih dari sekadar konstanta literal.

Seperti biasa, kode sumber lengkap untuk artikel ini dapat ditemukan di GitHub.