Utvide Enums i Java

1. Oversikt

Enum-typen, introdusert i Java 5, er en spesiell datatype som representerer en gruppe konstanter.

Ved å bruke enums kan vi definere og bruke konstantene våre i veien for typesikkerhet. Det bringer kompileringstidskontroll til konstantene.

Videre tillater det oss å bruke konstantene i bryterveske uttalelse.

I denne opplæringen vil vi diskutere utvidelse av enums i Java, for eksempel å legge til nye konstante verdier og nye funksjoner.

2. Enums og arv

Når vi vil utvide en Java-klasse, oppretter vi vanligvis en underklasse. I Java er også enums klasser.

I denne delen, la oss se om vi kan arve en enum som vi gjør med vanlige Java-klasser.

2.1. Utvide en Enum-type

La oss først se på et eksempel slik at vi raskt kan forstå problemet:

public enum BasicStringOperation {TRIM ("Fjern ledende og etterfølgende mellomrom."), TO_UPPER ("Endring av alle tegn til store bokstaver."), REVERSE ("Reversing the given string."); privat strengbeskrivelse; // konstruktør og getter}

Som koden ovenfor viser, har vi en enum BasicStringOperation som inneholder tre grunnleggende strengoperasjoner.

La oss si at vi vil legge til noen utvidelser i enum, for eksempel MD5_ENCODE og BASE64_ENCODE. Vi kan komme med denne enkle løsningen:

offentlig enum ExtendedStringOperation utvider BasicStringOperation {MD5_ENCODE ("Koding av den gitte strengen ved hjelp av MD5-algoritmen."), BASE64_ENCODE ("Koding av den gitte strengen ved bruk av BASE64-algoritmen."); privat strengbeskrivelse; // konstruktør og getter}

Når vi prøver å kompilere klassen, ser vi imidlertid kompileringsfeilen:

Kan ikke arve fra enum BasicStringOperation

2.2. Arv er ikke tillatt for Enums

La oss nå finne ut hvorfor vi fikk kompilatorfeilen.

Når vi kompilerer en enum, gjør Java-kompilatoren noe magisk med det:

  • Det gjør enum til en underklasse av den abstrakte klassen java.lang.Enum
  • Det kompilerer enum som en endelig klasse

For eksempel hvis vi demonterer det vi har samlet BasicStringOperation enum bruker javap, vi får se det er representert som en underklasse av java.lang.Enum:

$ javap BasicStringOperation public final class com.baeldung.enums.extendenum.BasicStringOperation utvider java.lang.Enum {public static final com.baeldung.enums.extendenum.BasicStringOperation TRIM; offentlig statisk endelig com.baeldung.enums.extendenum.BasicStringOperation TO_UPPER; offentlig statisk endelig com.baeldung.enums.extendenum.BasicStringOperation REVERSE; ...} 

Som vi vet, kan vi ikke arve a endelig klasse i Java. Dessuten, selv om vi kunne lage ExtendedStringOperation enum å arve BasicStringOperation, våre ExtendedStringOperation enum ville utvide to klasser: BasicStringOperation og java.lang.Enum. Det vil si at det ville bli en mangfoldig arvesituasjon, som ikke støttes i Java.

3. Etterlign utvidbare enumer med grensesnitt

Vi har lært at vi ikke kan lage en underklasse av en eksisterende enum. Imidlertid er et grensesnitt utvidbart. Derfor, vi kan etterligne utvidbare enumer ved å implementere et grensesnitt.

3.1. Etterlign utvide konstantene

For å forstå denne teknikken raskt, la oss ta en titt på hvordan vi kan etterligne utvide vår BasicStringOperation enum å ha MD5_ENCODE og BASE64_ENCODE operasjoner.

La oss først lage en grensesnittStringOperation:

offentlig grensesnitt StringOperation {String getDescription (); } 

Deretter får vi begge enumene til å implementere grensesnittet ovenfor:

public enum BasicStringOperation implementerer StringOperation {TRIM ("Fjerning av mellomrom og etterfølgende mellomrom."), TO_UPPER ("Endring av alle tegn i store bokstaver."), REVERSE ("Omvendt gitt streng."); privat strengbeskrivelse; // constructor and getter override} offentlig enum ExtendedStringOperation implementerer StringOperation {MD5_ENCODE ("Koding av den gitte strengen ved hjelp av MD5-algoritmen."), BASE64_ENCODE ("Koding av den gitte strengen ved hjelp av BASE64-algoritmen."); privat strengbeskrivelse; // constructor and getter override} 

Til slutt, la oss ta en titt på hvordan vi kan etterligne en utvidbar BasicStringOperation enum.

La oss si at vi har en metode i søknaden vår for å få beskrivelsen av BasicStringOperation enum:

public class Application {public String getOperationDescription (BasicStringOperation stringOperation) {return stringOperation.getDescription (); }} 

Nå kan vi endre parametertypen BasicStringOperation inn i grensesnitttypen StringOperation for å få metoden til å akseptere forekomster fra begge enums:

public String getOperationDescription (StringOperation stringOperation) {return stringOperation.getDescription (); }

3.2. Utvide funksjonalitet

Vi har sett hvordan vi kan etterligne utvidende konstanter av enums med grensesnitt.

Videre kan vi også legge til metoder i grensesnittet for å utvide funksjonaliteten til enumene.

For eksempel ønsker vi å utvide vår StringOperation enums slik at hver konstant faktisk kan bruke operasjonen til en gitt streng:

public class Application {public String applyOperation (StringOperation operation, String input) {return operation.apply (input); } // ...} 

For å oppnå det, la oss først legge til søke om() metode til grensesnittet:

offentlig grensesnitt StringOperation {String getDescription (); Streng gjelder (strenginngang); } 

Deretter lar vi hver StringOperation enum implementere denne metoden:

offentlig enum BasicStringOperation implementerer StringOperation {TRIM ("Fjerning av ledende og etterfølgende mellomrom.") {@Override public String gjelder (String input) {return input.trim (); }}, TO_UPPER ("Endring av alle tegn til store bokstaver.") {@Override public String gjelder (String input) {return input.toUpperCase (); }}, REVERSE ("Reversing the given string.") {@Override public String gjelder (String input) {return new StringBuilder (input) .reverse (). ToString (); }}; // ...} offentlig enum ExtendedStringOperation implementerer StringOperation {MD5_ENCODE ("Koding av den gitte strengen ved hjelp av MD5-algoritmen.") {@ Override public String gjelder (String input) {return DigestUtils.md5Hex (input); }}, BASE64_ENCODE ("Koding av den gitte strengen ved hjelp av BASE64-algoritmen.") {@ Override public String gjelder (String input) {return new String (new Base64 (). Encode (input.getBytes ())); }}; // ...} 

En testmetode viser at denne tilnærmingen fungerer som vi forventet:

@Test offentlig ugyldighet gittAStringAndOperation_whenApplyOperation_thenGetExpectedResult () {String input = "hallo"; String expectToUpper = "HELLO"; Streng forventetReverse = "olleh"; String expectTrim = "hei"; Streng forventetBase64 = "IGhlbGxv"; Streng forventetMd5 = "292a5af68d31c10e31ad449bd8f51263"; assertEquals (expectTrim, app.applyOperation (BasicStringOperation.TRIM, input)); assertEquals (expectToUpper, app.applyOperation (BasicStringOperation.TO_UPPER, input)); assertEquals (expectReverse, app.applyOperation (BasicStringOperation.REVERSE, input)); assertEquals (forventetBase64, app.applyOperation (ExtendedStringOperation.BASE64_ENCODE, input)); assertEquals (expectMd5, app.applyOperation (ExtendedStringOperation.MD5_ENCODE, input)); } 

4. Utvide en Enum uten å endre koden

Vi har lært hvordan vi kan utvide enum ved å implementere grensesnitt.

Noen ganger ønsker vi imidlertid å utvide funksjonalitetene til et enum uten å endre det. For eksempel vil vi utvide en enum fra et tredjepartsbibliotek.

4.1. Tilknytning av Enum-konstanter og grensesnittimplementeringer

La oss først se på et eksempel på enum:

public enum ImmutableOperation {REMOVE_WHITESPACES, TO_LOWER, INVERT_CASE} 

La oss si at enum er fra et eksternt bibliotek, derfor kan vi ikke endre koden.

Nå, i vår applikasjon klasse, vil vi ha en metode for å bruke den gitte operasjonen på inngangsstrengen:

public String applyImmutableOperation (ImmutableOperation operation, Streng input) {...}

Siden vi ikke kan endre enum-koden, Vi kan bruke EnumMap å knytte enumkonstantene og nødvendige implementeringer.

La oss først lage et grensesnitt:

offentlig grensesnitt Operatør {String gjelder (String input); } 

Deretter lager vi kartleggingen mellom enum-konstanter og Operatør implementeringer ved hjelp av en EnumMap:

offentlig klasse Søknad {privat statisk slutt Kart OPERATION_MAP; statisk {OPERATION_MAP = ny EnumMap (ImmutableOperation.class); OPERATION_MAP.put (ImmutableOperation.TO_LOWER, String :: toLowerCase); OPERATION_MAP.put (ImmutableOperation.INVERT_CASE, StringUtils :: swapCase); OPERATION_MAP.put (ImmutableOperation.REMOVE_WHITESPACES, input -> input.replaceAll ("\ s", "")); } public String applyImmutableOperation (ImmutableOperation operation, String input) {return operationMap.get (operation) .apply (input); }

På denne måten vår ApplyImmutableOperation () metoden kan bruke den tilsvarende operasjonen til den gitte inngangsstrengen:

@Test offentlig ugyldighet gittAStringAndImmutableOperation_whenApplyOperation_thenGetExpectedResult () {String input = "He ll O"; String expectToLower = "han kommer o"; Streng forventetRmWhitespace = "HellO"; Streng forventetInvertCase = "he LL o"; assertEquals (expectToLower, app.applyImmutableOperation (ImmutableOperation.TO_LOWER, input)); assertEquals (forventetRmWhitespace, app.applyImmutableOperation (ImmutableOperation.REMOVE_WHITESPACES, input)); assertEquals (forventetInvertCase, app.applyImmutableOperation (ImmutableOperation.INVERT_CASE, input)); } 

4.2. Validerer EnumMap Gjenstand

Nå, hvis enum er fra et eksternt bibliotek, vet vi ikke om det er endret eller ikke, for eksempel ved å legge til nye konstanter i enumet. I dette tilfellet, hvis vi ikke endrer initialiseringen av EnumMap å inneholde den nye enumverdien, vår EnumMap tilnærming kan komme inn i et problem hvis den nylig tilføyde enumkonstanten blir sendt til applikasjonen vår.

For å unngå det, kan vi validere EnumMap etter initialisering for å sjekke om den inneholder alle enum-konstanter:

statisk {OPERATION_MAP = ny EnumMap (ImmutableOperation.class); OPERATION_MAP.put (ImmutableOperation.TO_LOWER, String :: toLowerCase); OPERATION_MAP.put (ImmutableOperation.INVERT_CASE, StringUtils :: swapCase); // ImmutableOperation.REMOVE_WHITESPACES er ikke kartlagt hvis (Arrays.stream (ImmutableOperation.values ​​()). AnyMatch (it ->! OPERATION_MAP.containsKey (it))) {throw new IllegalStateException ("Unmapped enum constant found!"); }} 

Som koden ovenfor viser, om noen konstant fra Uforanderlig drift er ikke kartlagt, en IllegalStateException vil bli kastet. Siden validering er i en statisk blokkere, IllegalStateException vil være årsaken til ExceptionInInitializerError:

@Test offentlig ugyldighet gittUnmappedImmutableOperationValue_whenAppStarts_thenGetException () {Throwable throwable = assertThrows (ExceptionInInitializerError.class, () -> {ApplicationWithEx appEx = new ApplicationWithEx ();}); assertTrue (throwable.getCause () forekomst av IllegalStateException); } 

Så snart applikasjonen ikke starter med den nevnte feilen og årsaken, bør vi dobbeltsjekke Uforanderlig drift for å sikre at alle konstanter er kartlagt.

5. Konklusjon

Enum er en spesiell datatype i Java. I denne artikkelen har vi diskutert hvorfor enum ikke støtter arv. Etter det tok vi for oss hvordan vi kan etterligne utvidbare enumer med grensesnitt.

Vi har også lært hvordan vi utvider funksjonalitetene til et enum uten å endre det.

Som alltid er hele kildekoden til artikkelen tilgjengelig på GitHub.


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