Panduan untuk Jumlah Java

1. Gambaran keseluruhan

Dalam artikel ini, kita akan melihat apa yang dihitung Java, masalah apa yang mereka selesaikan dan bagaimana beberapa corak reka bentuk yang dapat mereka gunakan dalam praktik.

Kata kunci enum diperkenalkan di Java 5. Ini menunjukkan jenis kelas khas yang selalu meluaskan kelas java.lang.Enum . Untuk dokumentasi rasmi mengenai penggunaannya, lihat dokumentasinya.

Pemalar yang ditentukan dengan cara ini menjadikan kod lebih mudah dibaca, membenarkan pemeriksaan waktu kompilasi, mendokumenkan senarai nilai yang diterima dan mengelakkan tingkah laku yang tidak dijangka kerana nilai tidak sah dilewatkan.

Berikut adalah contoh enum yang ringkas dan ringkas yang menentukan status pesanan untuk pizza; status pesanan boleh DIPESAN , DIBACA atau DIHANTAR :

public enum PizzaStatus { ORDERED, READY, DELIVERED; }

Selain itu, mereka hadir dengan banyak kaedah yang berguna, yang mana anda harus tulis sendiri sekiranya anda menggunakan pemalar akhir statik awam tradisional.

2. Kaedah Enum Tersuai

OK, jadi sekarang kita mempunyai pemahaman asas tentang enum dan bagaimana anda boleh menggunakannya, mari kita ambil contoh sebelumnya ke tahap seterusnya dengan menentukan beberapa kaedah API tambahan pada enum:

public class Pizza { private PizzaStatus status; public enum PizzaStatus { ORDERED, READY, DELIVERED; } public boolean isDeliverable() { if (getStatus() == PizzaStatus.READY) { return true; } return false; } // Methods that set and get the status variable. } 

3. Membandingkan Jenis Enum Menggunakan Pengendali “==”

Oleh kerana jenis enum memastikan bahawa hanya satu contoh pemalar yang ada di JVM, kita boleh menggunakan operator “==” dengan selamat untuk membandingkan dua pemboleh ubah seperti yang dilihat dalam contoh di atas; lebih-lebih lagi pengendali “==” memberikan keselamatan masa kompilasi dan masa berjalan.

Mari kita pertama kita lihat pada keselamatan jangka masa dalam coretan berikut di mana "==" operator digunakan untuk membandingkan status dan NullPointerException tidak akan dibuang jika sama ada nilai adalah null . Sebaliknya NullPointerException akan dilemparkan jika kaedah sama digunakan:

if(testPz.getStatus().equals(Pizza.PizzaStatus.DELIVERED)); if(testPz.getStatus() == Pizza.PizzaStatus.DELIVERED); 

Bagi keselamatan masa kompilasi , mari kita lihat contoh lain di mana enum dari jenis yang berbeza dibandingkan dengan menggunakan kaedah sama ditentukan benar - kerana nilai enum dan kaedah getStatus secara kebetulan adalah sama, tetapi secara logiknya perbandingan haruslah salah. Masalah ini dielakkan dengan menggunakan operator “==”.

Penyusun akan menandakan perbandingan sebagai ralat ketidaksesuaian:

if(testPz.getStatus().equals(TestColor.GREEN)); if(testPz.getStatus() == TestColor.GREEN); 

4. Menggunakan Jenis Enum dalam Pernyataan Beralih

Jenis enum boleh digunakan dalam pernyataan suis juga:

public int getDeliveryTimeInDays() { switch (status) { case ORDERED: return 5; case READY: return 2; case DELIVERED: return 0; } return 0; }

5. Medan, Kaedah dan Pembina di Enums

Anda boleh menentukan pembina, kaedah, dan medan di dalam jenis enum yang menjadikannya sangat kuat.

Mari luaskan contoh di atas dan laksanakan peralihan dari satu tahap pizza ke tahap lain dan lihat bagaimana kita dapat menyingkirkan pernyataan if dan pernyataan peralihan yang digunakan sebelum ini:

public class Pizza { private PizzaStatus status; public enum PizzaStatus { ORDERED (5){ @Override public boolean isOrdered() { return true; } }, READY (2){ @Override public boolean isReady() { return true; } }, DELIVERED (0){ @Override public boolean isDelivered() { return true; } }; private int timeToDelivery; public boolean isOrdered() {return false;} public boolean isReady() {return false;} public boolean isDelivered(){return false;} public int getTimeToDelivery() { return timeToDelivery; } PizzaStatus (int timeToDelivery) { this.timeToDelivery = timeToDelivery; } } public boolean isDeliverable() { return this.status.isReady(); } public void printTimeToDeliver() { System.out.println("Time to delivery is " + this.getStatus().getTimeToDelivery()); } // Methods that set and get the status variable. } 

Coretan ujian di bawah menunjukkan cara ini berfungsi:

@Test public void givenPizaOrder_whenReady_thenDeliverable() { Pizza testPz = new Pizza(); testPz.setStatus(Pizza.PizzaStatus.READY); assertTrue(testPz.isDeliverable()); }

6. EnumSet dan EnumMap

6.1. Set Enum

The EnumSet ialah khusus Set pelaksanaan bertujuan untuk digunakan dengan Enum jenis.

Ia adalah perwakilan yang sangat cekap dan padat tertentu Set of Enum pemalar apabila dibandingkan dengan HashSet , oleh kerana dalaman Bit Vector Perwakilan yang digunakan. Dan ia menyediakan alternatif yang selamat untuk jenis "bendera bit" int- tradisional , yang membolehkan kita menulis kod ringkas yang lebih mudah dibaca dan dikekalkan.

The EnumSet adalah kelas abstrak yang mempunyai dua perlaksanaan dipanggil RegularEnumSet dan JumboEnumSet , salah satu yang dipilih adalah bergantung kepada bilangan pemalar dalam enum pada masa merta.

Oleh itu, adalah idea yang baik untuk menggunakan set ini setiap kali kita mahu bekerjasama dengan kumpulan pemalar enum dalam kebanyakan senario (seperti subset, menambah, membuang, dan untuk operasi pukal seperti mengandungAll dan removeAll ) dan menggunakan Enum.values ​​( ) jika anda hanya mahu melakukan berulang pada semua kemungkinan pemalar.

Dalam coretan kod di bawah, anda dapat melihat bagaimana EnumSet digunakan untuk membuat subset pemalar dan penggunaannya:

public class Pizza { private static EnumSet undeliveredPizzaStatuses = EnumSet.of(PizzaStatus.ORDERED, PizzaStatus.READY); private PizzaStatus status; public enum PizzaStatus { ... } public boolean isDeliverable() { return this.status.isReady(); } public void printTimeToDeliver() { System.out.println("Time to delivery is " + this.getStatus().getTimeToDelivery() + " days"); } public static List getAllUndeliveredPizzas(List input) { return input.stream().filter( (s) -> undeliveredPizzaStatuses.contains(s.getStatus())) .collect(Collectors.toList()); } public void deliver() { if (isDeliverable()) { PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy() .deliver(this); this.setStatus(PizzaStatus.DELIVERED); } } // Methods that set and get the status variable. } 

Menjalankan ujian berikut menunjukkan kekuatan pelaksanaan EnumSet antara muka Set :

@Test public void givenPizaOrders_whenRetrievingUnDeliveredPzs_thenCorrectlyRetrieved() { List pzList = new ArrayList(); Pizza pz1 = new Pizza(); pz1.setStatus(Pizza.PizzaStatus.DELIVERED); Pizza pz2 = new Pizza(); pz2.setStatus(Pizza.PizzaStatus.ORDERED); Pizza pz3 = new Pizza(); pz3.setStatus(Pizza.PizzaStatus.ORDERED); Pizza pz4 = new Pizza(); pz4.setStatus(Pizza.PizzaStatus.READY); pzList.add(pz1); pzList.add(pz2); pzList.add(pz3); pzList.add(pz4); List undeliveredPzs = Pizza.getAllUndeliveredPizzas(pzList); assertTrue(undeliveredPzs.size() == 3); }

6.2. Peta Enum

EnumMap adalah implementasi Peta khusus yang dimaksudkan untuk digunakan dengan pemalar enum sebagai kunci. Ini adalah pelaksanaan yang cekap dan padat dibandingkan dengan HashMap rakan sejawatnya dan secara dalaman ditunjukkan sebagai pelbagai:

EnumMap map; 

Mari kita lihat dengan cepat contoh sebenar yang menunjukkan bagaimana ia boleh digunakan dalam praktik:

public static EnumMap
    
      groupPizzaByStatus(List pizzaList) { EnumMap
     
       pzByStatus = new EnumMap
      
       (PizzaStatus.class); for (Pizza pz : pizzaList) { PizzaStatus status = pz.getStatus(); if (pzByStatus.containsKey(status)) { pzByStatus.get(status).add(pz); } else { List newPzList = new ArrayList(); newPzList.add(pz); pzByStatus.put(status, newPzList); } } return pzByStatus; } 
      
     
    

Melaksanakan ujian berikut menunjukkan kehebatan pelaksanaan EnumMap antara muka Peta :

@Test public void givenPizaOrders_whenGroupByStatusCalled_thenCorrectlyGrouped() { List pzList = new ArrayList(); Pizza pz1 = new Pizza(); pz1.setStatus(Pizza.PizzaStatus.DELIVERED); Pizza pz2 = new Pizza(); pz2.setStatus(Pizza.PizzaStatus.ORDERED); Pizza pz3 = new Pizza(); pz3.setStatus(Pizza.PizzaStatus.ORDERED); Pizza pz4 = new Pizza(); pz4.setStatus(Pizza.PizzaStatus.READY); pzList.add(pz1); pzList.add(pz2); pzList.add(pz3); pzList.add(pz4); EnumMap
    
      map = Pizza.groupPizzaByStatus(pzList); assertTrue(map.get(Pizza.PizzaStatus.DELIVERED).size() == 1); assertTrue(map.get(Pizza.PizzaStatus.ORDERED).size() == 2); assertTrue(map.get(Pizza.PizzaStatus.READY).size() == 1); }
    

7. Laksanakan Corak Reka Bentuk Menggunakan Enum

7.1. Corak Singleton

Biasanya, melaksanakan kelas menggunakan corak Singleton agak tidak remeh. Enum menyediakan cara yang mudah dan cepat untuk melaksanakan single.

In addition to that, since the enum class implements the Serializable interface under the hood, the class is guaranteed to be a singleton by the JVM, which unlike the conventional implementation where we have to ensure that no new instances are created during deserialization.

In the code snippet below, we see how we can implement singleton pattern:

public enum PizzaDeliverySystemConfiguration { INSTANCE; PizzaDeliverySystemConfiguration() { // Initialization configuration which involves // overriding defaults like delivery strategy } private PizzaDeliveryStrategy deliveryStrategy = PizzaDeliveryStrategy.NORMAL; public static PizzaDeliverySystemConfiguration getInstance() { return INSTANCE; } public PizzaDeliveryStrategy getDeliveryStrategy() { return deliveryStrategy; } }

7.2. Strategy Pattern

Conventionally the Strategy pattern is written by having an interface that is implemented by different classes.

Adding a new strategy meant adding a new implementation class. With enums, this is achieved with less effort, adding a new implementation means defining just another instance with some implementation.

The code snippet below shows how to implement the Strategy pattern:

public enum PizzaDeliveryStrategy { EXPRESS { @Override public void deliver(Pizza pz) { System.out.println("Pizza will be delivered in express mode"); } }, NORMAL { @Override public void deliver(Pizza pz) { System.out.println("Pizza will be delivered in normal mode"); } }; public abstract void deliver(Pizza pz); }

Add the following method to the Pizza class:

public void deliver() { if (isDeliverable()) { PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy() .deliver(this); this.setStatus(PizzaStatus.DELIVERED); } }
@Test public void givenPizaOrder_whenDelivered_thenPizzaGetsDeliveredAndStatusChanges() { Pizza pz = new Pizza(); pz.setStatus(Pizza.PizzaStatus.READY); pz.deliver(); assertTrue(pz.getStatus() == Pizza.PizzaStatus.DELIVERED); }

8. Java 8 and Enums

The Pizza class can be rewritten in Java 8, and you can see how the methods getAllUndeliveredPizzas() and groupPizzaByStatus() become so concise with the use of lambdas and the Stream APIs:

public static List getAllUndeliveredPizzas(List input) { return input.stream().filter( (s) -> !deliveredPizzaStatuses.contains(s.getStatus())) .collect(Collectors.toList()); } 
public static EnumMap
    
      groupPizzaByStatus(List pzList) { EnumMap
     
       map = pzList.stream().collect( Collectors.groupingBy(Pizza::getStatus, () -> new EnumMap(PizzaStatus.class), Collectors.toList())); return map; }
     
    

9. JSON Representation of Enum

Using Jackson libraries, it is possible to have a JSON representation of enum types as if they are POJOs. The code snippet below shows the Jackson annotations that can be used for the same:

@JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum PizzaStatus { ORDERED (5){ @Override public boolean isOrdered() { return true; } }, READY (2){ @Override public boolean isReady() { return true; } }, DELIVERED (0){ @Override public boolean isDelivered() { return true; } }; private int timeToDelivery; public boolean isOrdered() {return false;} public boolean isReady() {return false;} public boolean isDelivered(){return false;} @JsonProperty("timeToDelivery") public int getTimeToDelivery() { return timeToDelivery; } private PizzaStatus (int timeToDelivery) { this.timeToDelivery = timeToDelivery; } } 

We can use the Pizza and PizzaStatus as follows:

Pizza pz = new Pizza(); pz.setStatus(Pizza.PizzaStatus.READY); System.out.println(Pizza.getJsonString(pz)); 

to generate the following JSON representation of the Pizzas status:

{ "status" : { "timeToDelivery" : 2, "ready" : true, "ordered" : false, "delivered" : false }, "deliverable" : true }

For more information on JSON serializing/deserializing (including customization) of enum types refer to the Jackson – Serialize Enums as JSON Objects.

10. Conclusion

Dalam artikel ini, kami menjelajahi enum Java, dari asas bahasa hingga kes penggunaan dunia nyata yang lebih maju dan menarik.

Coretan kod dari artikel ini boleh didapati di repositori Github.