Sprievodca Java Enums

1. Prehľad

V tomto článku uvidíme, čo sú enumy Java, aké problémy riešia a ako je možné niektoré z návrhových vzorov použiť v praxi.

The enum kľúčové slovo bolo zavedené v prostredí Java 5. Označuje špeciálny typ triedy, ktorý vždy rozširuje java.lang.Enum trieda. Oficiálnu dokumentáciu o ich použití nájdete v dokumentácii.

Takto definované konštanty robia kód čitateľnejším, umožňujú kontrolu kompilácie, dokumentujú vopred zoznam akceptovaných hodnôt a zabraňujú neočakávanému správaniu v dôsledku odovzdávania neplatných hodnôt.

Tu je rýchly a jednoduchý príklad enumu, ktorý definuje stav objednávky na pizzu; stav objednávky môže byť OBJEDNANÉ, PRIPRAVENÝ alebo DODANÉ:

public enum PizzaStatus {OBJEDNANÉ, PRIPRAVENÉ, DORUČENÉ; }

Okrem toho prichádzajú s mnohými užitočnými metódami, ktoré by ste inak museli sami napísať, ak by ste používali tradičné verejné statické konečné konštanty.

2. Vlastné metódy výčtu

Dobre, takže keď už máme základné znalosti o tom, čo sú enumy a ako ich môžete použiť, vezmime náš predchádzajúci príklad na ďalšiu úroveň definovaním niektorých ďalších metód API vo enume:

verejná trieda Pizza {súkromný stav PizzaStatus; public enum PizzaStatus {OBJEDNANÉ, PRIPRAVENÉ, DORUČENÉ; } public boolean isDeliverable () {if (getStatus () == PizzaStatus.READY) {return true; } return false; } // Metódy, ktoré nastavujú a získavajú stavovú premennú. } 

3. Porovnanie typov výčtu pomocou operátora „==“

Pretože typy enum zabezpečujú, aby v JVM existovala iba jedna inštancia konštánt, môžeme bezpečne použiť operátor „==“ na porovnanie dvoch premenných, ako je vidieť v príklade vyššie; operátor „==“ navyše poskytuje bezpečnosť počas kompilácie a za behu.

Najprv sa pozrime pri bezpečnosti chodu v nasledujúcom úryvku, kde sa operátor „==“ používa na porovnanie stavov a NullPointerException nebude vyhodená, ak je ktorákoľvek hodnota nulový. Naopak an NullPointerException by bola vyhodená, keby bola použitá metóda equals:

if (testPz.getStatus (). sa rovná (Pizza.PizzaStatus.DELIVERED)); if (testPz.getStatus () == Pizza.PizzaStatus.DELIVERED); 

Ako pre zostaviť časovú bezpečnosť, pozrime sa na ďalší príklad, kde sa enum iného typu porovnáva pomocou znaku rovná sa metóda je určená ako pravdivá - pretože hodnoty enumu a getStatus metóda je zhodou okolností rovnaká, ale logicky by malo byť porovnanie nepravdivé. Tomuto problému sa dá vyhnúť použitím operátora „==“.

Kompilátor nahlási porovnanie ako chybu nekompatibility:

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

4. Používanie typov Enum v príkazoch Switch

Typy Enum je možné použiť v a prepínač vyhlásenia tiež:

public int getDeliveryTimeInDays () {switch (status) {case ORDERED: return 5; prípad PRIPRAVENÝ: návrat 2; puzdro DORUČENÉ: návrat 0; } návrat 0; }

5. Polia, metódy a konštruktory vo výčtoch

Môžete definovať konštruktory, metódy a polia vo vnútri typov enum, vďaka ktorým je veľmi výkonný.

Poďme rozšíriť vyššie uvedený príklad a implementovať prechod z jedného stupňa pizze do druhého a zistiť, ako sa ho môžeme zbaviť ak vyhlásenie a prepínač vyhlásenie použité skôr:

verejná trieda Pizza {súkromný stav PizzaStatus; 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 ("Čas doručenia je" + this.getStatus (). getTimeToDelivery ()); } // Metódy, ktoré nastavujú a získavajú stavovú premennú. } 

Fungujúci úryvok nižšie ukazuje, ako to funguje:

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

6. EnumSet a EnumMap

6.1. EnumSet

The EnumSet je špecializovaný Nastaviť implementácia určená na použitie s Enum typy.

Je to veľmi efektívne a kompaktné znázornenie konkrétneho Nastaviť z Enum konštanty v porovnaní s a HashSet, z dôvodu interného Bitové vektorové zastúpenie ktorý sa používa. A poskytuje typovo bezpečnú alternatívu k tradičnej int„bitové príznaky“ na základe, ktoré nám umožňujú písať stručný kód, ktorý je čitateľnejší a udržiavateľnejší.

The EnumSet je abstraktná trieda, ktorá má dve implementácie tzv RegularEnumSet a JumboEnumSet, z ktorých jedna je zvolená v závislosti od počtu konštánt vo výčnelku v čase vytvorenia inštancie.

Preto je vždy dobré použiť túto sadu, kedykoľvek chceme pracovať so zbierkou konštánt enum vo väčšine scenárov (ako je podmnožina, pridávanie, odstraňovanie a pre hromadné operácie, ako napríklad obsahujeVšetko a odobrať všetky) a použitie Enum.values ​​() ak chcete iba iterovať cez všetky možné konštanty.

V útržku kódu nižšie môžete vidieť, ako na to EnumSet sa používa na vytvorenie podmnožiny konštánt a jej použitia:

verejná trieda Pizza {private static EnumSet undeliveredPizzaStatuses = EnumSet.of (PizzaStatus.ORDERED, PizzaStatus.READY); súkromný stav PizzaStatus; public enum PizzaStatus {...} public boolean isDeliverable () {return this.status.isReady (); } public void printTimeToDeliver () {System.out.println ("Čas doručenia je" + this.getStatus (). getTimeToDelivery () + "dni"); } 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); }} // Metódy, ktoré nastavujú a získavajú stavovú premennú. } 

Vykonaním nasledujúceho testu sa preukázala sila EnumSet implementácia Nastaviť rozhranie:

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

6.2. EnumMap

EnumMap je špecializovaný Mapa implementácia sa mala používať s konštantami enum ako kľúčmi. Je to efektívna a kompaktná implementácia v porovnaní s jej náprotivkom HashMap a je interne predstavovaná ako pole:

Mapa EnumMap; 

Poďme sa rýchlo pozrieť na skutočný príklad, ktorý ukazuje, ako sa dá využiť v praxi:

verejná statická EnumMap groupPizzaByStatus (Zoznam pizzaList) {EnumMap pzByStatus = nový EnumMap(PizzaStatus.class); pre (Pizza pz: pizzaList) {stav PizzaStatus = pz.getStatus (); if (pzByStatus.containsKey (status)) {pzByStatus.get (status) .add (pz); } else {List newPzList = new ArrayList (); newPzList.add (pz); pzByStatus.put (status, newPzList); }} vratit pzByStatus; } 

Vykonaním nasledujúceho testu sa preukázala sila EnumMap implementácia Mapa rozhranie:

@Test public void givenPizaOrders_whenGroupByStatusCalled_thenCorrectlyGrouped () {List pzList = new ArrayList (); Pizza pz1 = nová Pizza (); pz1.setStatus (Pizza.PizzaStatus.DELIVERED); Pizza pz2 = nová pizza (); pz2.setStatus (Pizza.PizzaStatus.ORDERED); Pizza pz3 = nová pizza (); pz3.setStatus (Pizza.PizzaStatus.ORDERED); Pizza pz4 = nová pizza (); pz4.setStatus (Pizza.PizzaStatus.READY); pzList.add (pz1); pzList.add (pz2); pzList.add (pz3); pzList.add (pz4); EnumMap mapa = 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. Implementujte návrhové vzory pomocou výčtov

7.1. Singleton vzor

Za normálnych okolností je implementácia triedy pomocou Singletonovho vzoru dosť nepodstatná. Enums poskytujú ľahký a rýchly spôsob implementácie singletonov.

Okrem toho, pretože trieda enum implementuje Serializovateľné rozhranie pod kapotou, trieda zaručuje, že JVM bude singleton, ktorý na rozdiel od konvenčnej implementácie, kde musíme zabezpečiť, aby sa počas deserializácie nevytvárali žiadne nové inštancie.

V útržku kódu nižšie vidíme, ako môžeme implementovať singletonový vzor:

public enum PizzaDeliverySystemConfiguration {INSTANCE; PizzaDeliverySystemConfiguration () {// Konfigurácia inicializácie, ktorá zahŕňa // prepísanie predvolených nastavení, ako je stratégia doručenia} private PizzaDeliveryStrategy deliveryStrategy = PizzaDeliveryStrategy.NORMAL; public static PizzaDeliverySystemConfiguration getInstance () {návrat INSTANCE; } verejná PizzaDeliveryStrategy getDeliveryStrategy () {návrat deliveryStrategy; }}

7.2. Strategický vzor

Vzor stratégie sa obvykle píše tak, že má rozhranie implementované rôznymi triedami.

Pridanie novej stratégie znamenalo pridanie novej triedy implementácie. Pomocou enums sa to dá dosiahnuť s menším úsilím, pridanie novej implementácie znamená definovanie len inej inštancie s určitou implementáciou.

Fragment kódu nižšie ukazuje, ako implementovať vzor stratégie:

public enum PizzaDeliveryStrategy {EXPRESS {@Override public void deliver (Pizza pz) {System.out.println ("Pizza bude doručená v expresnom režime"); }}, NORMÁLNE {@Override public void deliver (Pizza pz) {System.out.println ("Pizza bude doručená v normálnom režime"); }}; verejné doručenie abstraktných prázdnin (Pizza pz); }

Pridajte nasledujúcu metódu do Pizza trieda:

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

8. Java 8 a výčty

The Pizza triedy je možné prepísať v prostredí Java 8 a vidíte, ako sú tieto metódy getAllUndeliveredPizzas () a groupPizzaByStatus () s použitím lambdas a Prúd API:

public static List getAllUndeliveredPizzas (List input) {return input.stream (). filter ((s) ->! deliverPizzaStatuses.contains (s.getStatus ())) .collect (Collectors.toList ()); } 
verejná statická EnumMap groupPizzaByStatus (Zoznam pzList) {EnumMap map = pzList.stream (). collect (Collectors.groupingBy (Pizza :: getStatus, () -> nový EnumMap (PizzaStatus.class), Collectors.toList ())); spiatočná mapa; }

9. Zastúpenie spoločnosti JSON z organizácie Enum

Pomocou Jacksonových knižníc je možné mať JSON reprezentáciu typov enum, akoby išlo o POJO. Fragment kódu nižšie zobrazuje Jacksonove anotácie, ktoré je možné použiť pre rovnaké:

@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; } súkromné ​​PizzaStatus (int timeToDelivery) {this.timeToDelivery = timeToDelivery; }} 

Pizza a PizzaStatus môžeme použiť nasledovne:

Pizza pz = nová pizza (); pz.setStatus (Pizza.PizzaStatus.READY); System.out.println (Pizza.getJsonString (pz)); 

vygenerovať nasledujúcu reprezentáciu JSON súboru Pizzastav:

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

Ďalšie informácie o serializácii / deserializácii súborov JSON (vrátane prispôsobenia) typov enum nájdete v dokumentácii Jackson - Serialize Enums ako objekty JSON.

10. Záver

V tomto článku sme preskúmali výčet Java, od jazykových základov až po pokročilejšie a zaujímavé prípady použitia v reálnom svete.

Útržky kódu z tohto článku nájdete v úložisku Github.


$config[zx-auto] not found$config[zx-overlay] not found