1. Pengenalan
Jenis Java enum menyediakan cara yang disokong bahasa untuk membuat dan menggunakan nilai tetap. Dengan menentukan satu set nilai yang terbatas, enum lebih selamat dari jenis berbanding pemboleh ubah literal tetap seperti String atau int .
Walau bagaimanapun, nilai enum diperlukan sebagai pengecam yang sah , dan kami digalakkan untuk menggunakan SCREAMING_SNAKE_CASE secara konvensional.
Memandangkan batasan tersebut, nilai enum saja tidak sesuai untuk rentetan yang boleh dibaca manusia atau nilai bukan tali .
Dalam tutorial ini, kita akan menggunakan ciri enum sebagai kelas Java untuk melampirkan nilai yang kita mahukan.
2. Menggunakan Java Enum sebagai Kelas
Kami sering membuat enum sebagai senarai nilai yang mudah. Sebagai contoh, berikut adalah dua baris pertama jadual berkala sebagai enum ringkas :
public enum Element { H, HE, LI, BE, B, C, N, O, F, NE }
Dengan menggunakan sintaks di atas, kami telah membuat sepuluh contoh terakhir enum bernama Element . Walaupun ini sangat cekap, kami hanya menangkap simbol elemen. Dan walaupun bentuk huruf besar sesuai untuk pemalar Java, bukan seperti biasanya kita menulis simbol.
Selain itu, kami juga kehilangan sifat lain dari unsur jadual berkala, seperti nama dan berat atom.
Walaupun jenis enum mempunyai tingkah laku khas di Java, kita dapat menambahkan konstruktor, medan, dan kaedah seperti yang kita lakukan dengan kelas lain. Oleh kerana itu, kita dapat meningkatkan enum untuk memasukkan nilai-nilai yang kita perlukan.
3. Menambah Pembina dan Medan Akhir
Mari mulakan dengan menambahkan nama elemen. Kami akan menetapkan nama menjadi pemboleh ubah akhir menggunakan konstruktor :
public enum Element { H("Hydrogen"), HE("Helium"), // ... NE("Neon"); public final String label; private Element(String label) { this.label = label; } }
Pertama sekali, kita melihat sintaks khas dalam senarai pengisytiharan. Ini adalah bagaimana konstruktor dipanggil untuk jenis enum . Walaupun tidak sah menggunakan operator baru untuk enum , kami dapat menyampaikan argumen pembina dalam senarai pengisytiharan.
Kami kemudian menyatakan label pemboleh ubah contoh . Terdapat beberapa perkara yang perlu diberi perhatian.
Pertama, kami memilih pengecam label dan bukan namanya . Walaupun nama medan ahli tersedia untuk digunakan, mari pilih label untuk mengelakkan kekeliruan dengan kaedah Enum.name () yang telah ditetapkan .
Kedua, medan label kami adalah muktamad . Walaupun bidang enum tidak harus muktamad , dalam kebanyakan kes, kami tidak mahu label kami berubah. Dalam semangat nilai enum menjadi tetap, ini masuk akal.
Akhirnya, medan label adalah umum. Oleh itu, kita boleh mengakses label secara langsung:
System.out.println(BE.label);
Sebaliknya, bidang ini boleh menjadi peribadi , diakses dengan kaedah getLabel () . Untuk tujuan singkat, artikel ini akan terus menggunakan gaya bidang awam.
4. Mencari Nilai Enum Java
Java menyediakan kaedah valueOf (String) untuk semua jenis enum . Oleh itu, kita selalu dapat memperoleh nilai enum berdasarkan nama yang dinyatakan:
assertSame(Element.LI, Element.valueOf("LI"));
Walau bagaimanapun, kami mungkin ingin mencari nilai enum mengikut bidang label kami juga. Untuk melakukan itu kita boleh menambah kaedah statik :
public static Element valueOfLabel(String label) { for (Element e : values()) { if (e.label.equals(label)) { return e; } } return null; }
Statik valueOfLabel () kaedah lelaran yang Element nilai sehingga ia mendapati perlawanan. Ia mengembalikan null jika ada perlawanan dijumpai. Sebaliknya, pengecualian boleh dilemparkan dan bukannya mengembalikan nol .
Mari lihat contoh ringkas menggunakan kaedah valueOfLabel () kami :
assertSame(Element.LI, Element.valueOfLabel("Lithium"));
5. Mencapai Nilai Pencarian
Kita boleh mengelakkan pengulangan nilai enum dengan menggunakan Peta untuk menyimpan label . Untuk melakukan ini, kami menentukan Peta akhir yang statik 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); } }
Hasil cache, nilai enum diulang sekali sahaja , dan kaedah valueOfLabel () dipermudahkan.
Sebagai alternatif, kita dapat dengan malas membina cache ketika pertama kali diakses dalam kaedah valueOfLabel () . Sekiranya demikian, akses peta mesti diselaraskan untuk mengelakkan masalah bersamaan.
6. Melampirkan Pelbagai Nilai
The Enum pembina boleh menerima pelbagai nilai . Untuk menggambarkan, mari tambah nombor atom sebagai int dan berat atom sebagai apungan :
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); } }
Begitu juga, kita dapat menambahkan nilai yang kita inginkan ke enum , seperti simbol huruf besar, "Dia", "Li", dan "Jadilah", misalnya.
Lebih-lebih lagi, kita dapat menambahkan nilai yang dihitung ke enum kita dengan menambahkan kaedah untuk menjalankan operasi.
7. Mengawal Antara Muka
Hasil daripada menambahkan bidang dan kaedah ke enum kami , kami telah menukar antara muka awamnya. Oleh itu, kod kami, yang menggunakan kaedah inti Enum name () dan valueOf () , tidak akan mengetahui bidang baru kami.
Kaedah static valueOf () sudah ditentukan untuk kita oleh bahasa Java. Oleh itu, kami tidak dapat memberikan pelaksanaan valueOf () kami sendiri .
Begitu juga, kerana kaedah Enum.name () adalah muktamad, kita juga tidak boleh menggantinya .
Hasilnya, tidak ada cara praktikal untuk menggunakan bidang tambahan kami menggunakan API Enum standard . Sebaliknya, mari kita lihat beberapa cara yang berbeza untuk mendedahkan bidang kita.
7.1. Mengatasi toString ()
Overriding toString () mungkin merupakan alternatif untuk mengganti nama () :
@Override public String toString() { return this.label; }
Secara lalai, Enum.toString () mengembalikan nilai yang sama dengan Enum.name ().
7.2. Melaksanakan Antaramuka
The enum taip Java boleh melaksanakan antara muka . Walaupun pendekatan ini tidak begitu umum seperti Enum API, antara muka membantu kami membuat generalisasi.
Mari pertimbangkan antara muka ini:
public interface Labeled { String label(); }
Untuk konsisten dengan kaedah Enum.name () , kaedah label kami () tidak mempunyai awalan get .
Dan, kerana kaedah valueOfLabel () adalah statik , kami tidak memasukkannya ke dalam antara muka kami.
Akhirnya, kami dapat melaksanakan antara muka di enum kami :
public enum Element implements Labeled { // ... @Override public String label() { return label; } // ... }
Satu kelebihan pendekatan ini ialah antara muka Labeled dapat digunakan untuk kelas mana pun, bukan hanya jenis enum . Daripada bergantung pada Enum API generik , kami sekarang mempunyai API yang lebih khusus konteks.
8. Kesimpulannya
Dalam artikel ini, kami telah meneroka banyak ciri pelaksanaan Java Enum . Dengan menambahkan konstruktor, medan, dan kaedah, kita melihat bahawa enum dapat melakukan lebih banyak daripada pemalar literal.
Seperti biasa, kod sumber penuh untuk artikel ini boleh didapati di GitHub.