En guide til Java Enums

1. Oversikt

I denne artikkelen vil vi se hva Java-enums er, hvilke problemer de løser og hvordan noen av designmønstrene de kan brukes i praksis.

De enum nøkkelord ble introdusert i Java 5. Det betegner en spesiell type klasse som alltid utvider java.lang.Enum klasse. For å få den offisielle dokumentasjonen for bruken, se på dokumentasjonen.

Konstanter definert på denne måten gjør koden mer lesbar, tillater kontroll av kompileringstid, dokumenterer forhåndslisten over aksepterte verdier og unngår uventet oppførsel på grunn av ugyldige verdier som sendes inn.

Her er et raskt og enkelt eksempel på en enum som definerer statusen for en bestilling av en pizza; ordrestatus kan være BESTILT, KLAR eller LEVERT:

offentlig enum PizzaStatus {BESTILT, KLAR, LEVERT; }

I tillegg kommer de med mange nyttige metoder, som du ellers måtte skrive selv hvis du brukte tradisjonelle offentlige statiske endelige konstanter.

2. Egendefinerte Enum-metoder

OK, så nå som vi har en grunnleggende forståelse av hva enums er og hvordan du kan bruke dem, la oss ta vårt forrige eksempel til neste nivå ved å definere noen ekstra API-metoder på enum:

offentlig klasse Pizza {privat PizzaStatus-status; offentlig enum PizzaStatus {BESTILT, KLAR, LEVERT; } public boolean isDeliverable () {if (getStatus () == PizzaStatus.READY) {return true; } returner falsk; } // Metoder som angir og får statusvariabelen. } 

3. Sammenligning av enumtyper ved bruk av “==” operatør

Siden enumtyper sørger for at bare en forekomst av konstantene finnes i JVM, kan vi trygt bruke “==” -operatøren til å sammenligne to variabler som vist i eksemplet ovenfor; dessuten gir operatøren “==” kompileringstid og kjøretidssikkerhet.

La oss først se ved kjøretids sikkerhet i følgende kodebit der operatøren “==” brukes til å sammenligne statuser og a NullPointerException vil ikke bli kastet hvis noen av verdiene er null. Omvendt er an NullPointerException ville bli kastet hvis likemetoden ble brukt:

hvis (testPz.getStatus (). tilsvarer (Pizza.PizzaStatus.DELIVERED)); hvis (testPz.getStatus () == Pizza.PizzaStatus.DELIVERED); 

Når det gjelder kompilere tidssikkerhet, la oss se på et annet eksempel der en enum av en annen type sammenlignes med er lik metoden er bestemt for å være sant - fordi verdiene til enum og getStatus metoden tilfeldigvis er den samme, men logisk sett skal sammenligningen være falsk. Dette problemet unngås ved å bruke operatøren “==”.

Kompilatoren vil markere sammenligningen som en inkompatibilitetsfeil:

hvis (testPz.getStatus (). er lik (TestColor.GREEN)); hvis (testPz.getStatus () == TestColor.GREEN); 

4. Bruke enumtyper i bryteruttalelser

Enumtyper kan brukes i en bytte om uttalelser også:

public int getDeliveryTimeInDays () {switch (status) {case BESTILLT: retur 5; sak KLAR: retur 2; sak LEVERT: retur 0; } returner 0; }

5. Felt, metoder og konstruktører i Enums

Du kan definere konstruktører, metoder og felt i enumtyper som gjør det veldig kraftig.

La oss utvide eksemplet ovenfor og implementere overgangen fra ett trinn i en pizza til et annet og se hvordan vi kan bli kvitt hvis uttalelse og bytte om uttalelse brukt før:

offentlig klasse Pizza {privat PizzaStatus-status; offentlig enum PizzaStatus {BESTILT (5) {@Override offentlig boolsk erBestilt () {return true; }}, READY (2) {@Override public boolean isReady () {return true; }}, LEVERT (0) {@Override public boolean isDelivered () {return true; }}; privat 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 (); } offentlig ugyldig printTimeToDeliver () {System.out.println ("Tid for levering er" + this.getStatus (). getTimeToDelivery ()); } // Metoder som angir og får statusvariabelen. } 

Testutdraget nedenfor viser hvordan dette fungerer:

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

6. EnumSet og EnumMap

6.1. EnumSet

De EnumSet er spesialist Sett implementering ment å brukes med Enum typer.

Det er en veldig effektiv og kompakt fremstilling av et bestemt Sett av Enum konstanter sammenlignet med a HashSet, på grunn av det indre Bit Vector Representasjon som brukes. Og det gir et typesikkert alternativ til tradisjonelt int-baserte "bitflagg", slik at vi kan skrive kortfattet kode som er mer lesbar og vedlikeholdbar.

De EnumSet er en abstrakt klasse som har to implementeringer kalt RegularEnumSet og JumboEnumSethvorav den ene er valgt avhengig av antall konstanter i enum på tidspunktet for instantiering.

Derfor er det alltid en god ide å bruke dette settet når vi vil jobbe med en samling enumkonstanter i de fleste scenarier (som delmengde, legge til, fjerne og for bulkoperasjoner som inneholderAlle og Fjern alle) og bruk Enum.values ​​() hvis du bare vil gjenta alle mulige konstanter.

I kodebiten nedenfor kan du se hvordan EnumSet brukes til å lage et delsett av konstanter og dets bruk:

offentlig klasse Pizza {privat statisk EnumSet undeliveredPizzaStatuses = EnumSet.of (PizzaStatus.ORDERED, PizzaStatus.READY); privat PizzaStatus-status; public enum PizzaStatus {...} public boolean isDeliverable () {return this.status.isReady (); } offentlig ugyldig printTimeToDeliver () {System.out.println ("Tid for levering er" + this.getStatus (). getTimeToDelivery () + "dager"); } offentlig statisk liste 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); }} // Metoder som angir og får statusvariabelen. } 

Å utføre følgende test demonstrerte kraften til EnumSet gjennomføring av Sett grensesnitt:

@Test offentlig ugyldig gittPizaOrders_whenRetrievingUnDeliveredPzs_thenCorrectlyRetrieved () {List pzList = new ArrayList (); Pizza pz1 = ny Pizza (); pz1.setStatus (Pizza.PizzaStatus.DELIVERED); Pizza pz2 = ny Pizza (); pz2.setStatus (Pizza.PizzaStatus.ORDRERT); Pizza pz3 = ny Pizza (); pz3.setStatus (Pizza.PizzaStatus.ORDERED); Pizza pz4 = ny 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. EnumMap

EnumMap er spesialist Kart implementering ment å brukes med enum-konstanter som nøkler. Det er en effektiv og kompakt implementering sammenlignet med motparten HashMap og er internt representert som en matrise:

EnumMap-kart; 

La oss se raskt på et reelt eksempel som viser hvordan det kan brukes i praksis:

offentlig statisk EnumMap groupPizzaByStatus (Liste pizzaListe) {EnumMap pzByStatus = ny EnumMap(PizzaStatus.klasse); for (Pizza pz: pizzaList) {PizzaStatus status = pz.getStatus (); hvis (pzByStatus.containsKey (status)) {pzByStatus.get (status) .add (pz); } annet {List newPzList = new ArrayList (); newPzList.add (pz); pzByStatus.put (status, newPzList); }} returner pzByStatus; } 

Å utføre følgende test demonstrerte kraften til EnumMap gjennomføring av Kart grensesnitt:

@Test offentlig ugyldig gittPizaOrders_whenGroupByStatusCalled_thenCorrectlyGrouped () {List pzList = new ArrayList (); Pizza pz1 = ny Pizza (); pz1.setStatus (Pizza.PizzaStatus.DELIVERED); Pizza pz2 = ny Pizza (); pz2.setStatus (Pizza.PizzaStatus.ORDRERT); Pizza pz3 = ny Pizza (); pz3.setStatus (Pizza.PizzaStatus.ORDERED); Pizza pz4 = ny Pizza (); pz4.setStatus (Pizza.PizzaStatus.READY); pzList.add (pz1); pzList.add (pz2); pzList.add (pz3); pzList.add (pz4); EnumMap kart = Pizza.groupPizzaByStatus (pzList); assertTrue (map.get (Pizza.PizzaStatus.DELIVERED) .størrelse () == 1); assertTrue (map.get (Pizza.PizzaStatus.ORDERED) .størrelse () == 2); assertTrue (map.get (Pizza.PizzaStatus.READY) .størrelse () == 1); }

7. Implementere designmønstre ved hjelp av Enums

7.1. Singleton mønster

Normalt er implementering av en klasse ved hjelp av Singleton-mønsteret ganske ikke trivielt. Enums gir en enkel og rask måte å implementere singletons på.

I tillegg til det, siden enum-klassen implementerer Serialiserbar grensesnitt under panseret, er klassen garantert en singleton av JVM, som i motsetning til den konvensjonelle implementeringen der vi må sikre at ingen nye forekomster blir opprettet under deserialisering.

I kodebiten nedenfor ser vi hvordan vi kan implementere singleton-mønster:

offentlig enum PizzaDeliverySystemConfiguration {INSTANCE; PizzaDeliverySystemConfiguration () {// Initialiseringskonfigurasjon som involverer // overordnede standarder som leveringsstrategi} privat PizzaDeliveryStrategy deliveryStrategy = PizzaDeliveryStrategy.NORMAL; offentlig statisk PizzaDeliverySystemConfiguration getInstance () {return INSTANCE; } offentlig PizzaDeliveryStrategy getDeliveryStrategy () {retur leveringStrategy; }}

7.2. Strategimønster

Konvensjonelt er strategimønsteret skrevet ved å ha et grensesnitt som er implementert av forskjellige klasser.

Å legge til en ny strategi betydde å legge til en ny implementeringsklasse. Med enums oppnås dette med mindre innsats. Å legge til en ny implementering betyr å definere bare en annen forekomst med noe implementering.

Kodebiten nedenfor viser hvordan du implementerer strategimønsteret:

offentlig enum PizzaDeliveryStrategy {EXPRESS {@Override public void deliver (Pizza pz) {System.out.println ("Pizza blir levert i ekspressmodus"); }}, NORMAL {@Override public void deliver (Pizza pz) {System.out.println ("Pizza blir levert i normal modus"); }}; offentlig abstrakt ugyldig levering (Pizza pz); }

Legg til følgende metode i Pizza klasse:

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

8. Java 8 og Enums

De Pizza klasse kan skrives om i Java 8, og du kan se hvordan metodene getAllUndeliveredPizzas () og gruppePizzaByStatus () bli så kortfattet med bruk av lambdas og Strøm APIer:

offentlig statisk liste getAllUndeliveredPizzas (List input) {return input.stream (). filter ((s) ->! DeliverPizzaStatuses.contains (s.getStatus ())) .collect (Collectors.toList ()); } 
offentlig statisk EnumMap groupPizzaByStatus (Liste pzList) {EnumMap map = pzList.stream (). collect (Collectors.groupingBy (Pizza :: getStatus, () -> new EnumMap (PizzaStatus.class), Collectors.toList ())); retur kart; }

9. JSON Representasjon av Enum

Ved hjelp av Jackson-biblioteker er det mulig å ha en JSON-representasjon av enumtyper som om de er POJOer. Kodebiten nedenfor viser Jackson-merknadene som kan brukes til det samme:

@JsonFormat (form = JsonFormat.Shape.OBJECT) offentlig enum PizzaStatus {BESTILT (5) {@Override offentlig boolsk erBestilt () {return true; }}, READY (2) {@Override public boolean isReady () {return true; }}, LEVERT (0) {@Override public boolean isDelivered () {return true; }}; privat int timeToDelivery; public boolean isOrdered () {return false;} public boolean isReady () {return false;} public boolean isDelivered () {return false;} @JsonProperty ("timeToDelivery") public int getTimeToDelivery () {return timeToDelivery; } privat PizzaStatus (int timeToDelivery) {this.timeToDelivery = timeToDelivery; }} 

Vi kan bruke Pizza og PizzaStatus som følger:

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

for å generere følgende JSON-representasjon av Pizzas status:

{"status": {"timeToDelivery": 2, "ready": true, "order": false, "Delivered": false}, "Deliverable": True}

For mer informasjon om JSON serialisering / deserialisering (inkludert tilpasning) av enumtyper, se Jackson - Serialize Enums som JSON Objects.

10. Konklusjon

I denne artikkelen utforsket vi Java enum, fra grunnleggende språk til mer avanserte og interessante virkelige brukssaker.

Kodebiter fra denne artikkelen finner du i Github-depotet.


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