Udvidelse af Enums i Java

1. Oversigt

Enum-typen, der blev introduceret i Java 5, er en speciel datatype, der repræsenterer en gruppe konstanter.

Ved hjælp af enums kan vi definere og bruge vores konstanter som typesikkerhed. Det bringer konstant tidskontrol til konstanterne.

Yderligere giver det os mulighed for at bruge konstanterne i afbryderkasse udmelding.

I denne vejledning diskuterer vi udvidelse af enums i Java, f.eks. Tilføjelse af nye konstante værdier og nye funktioner.

2. Enums og arv

Når vi vil udvide en Java-klasse, opretter vi typisk en underklasse. I Java er enums også klasser.

Lad os i dette afsnit se, om vi kan arve et enum, som vi gør med almindelige Java-klasser.

2.1. Udvidelse af en Enum-type

Lad os først se på et eksempel, så vi hurtigt kan forstå problemet:

public enum BasicStringOperation {TRIM ("Fjernelse af forreste og bageste mellemrum."), TO_UPPER ("Ændring af alle tegn til store bogstaver."), REVERSE ("Vender den givne streng."); privat streng beskrivelse; // konstruktør og getter}

Som koden ovenfor viser, har vi et enum BasicStringOperation der indeholder tre grundlæggende strengoperationer.

Lad os nu sige, at vi vil tilføje en udvidelse til enum, såsom MD5_ENCODE og BASE64_ENCODE. Vi kan komme med denne ligefremme løsning:

offentlig enum ExtendedStringOperation udvider BasicStringOperation {MD5_ENCODE ("Kodning af den givne streng ved hjælp af MD5-algoritmen."), BASE64_ENCODE ("Kodning af den givne streng ved hjælp af BASE64-algoritmen."); privat streng beskrivelse; // konstruktør og getter}

Når vi forsøger at kompilere klassen, ser vi dog kompileringsfejlen:

Kan ikke arve fra enum BasicStringOperation

2.2. Arv er ikke tilladt for Enums

Lad os nu finde ud af, hvorfor vi fik vores kompilatorfejl.

Når vi kompilerer et enum, gør Java-kompileren noget magisk ved det:

  • Det gør enum til en underklasse af den abstrakte klasse java.lang.Enum
  • Det kompilerer enum som en endelig klasse

For eksempel, hvis vi adskiller vores kompilerede BasicStringOperation enum bruger javap, vi ser det er repræsenteret som en underklasse af java.lang.Enum:

$ javap BasicStringOperation offentlig endelig klasse com.baeldung.enums.extendenum.BasicStringOperation udvider java.lang.Enum {offentlig statisk endelig 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 ved, kan vi ikke arve en endelig klasse i Java. Desuden, selvom vi kunne skabe ExtendedStringOperation enum at arve BasicStringOperation, vores ExtendedStringOperation enum ville udvide to klasser: BasicStringOperation og java.lang.Enum. Det vil sige, det ville blive en multipel arvssituation, som ikke understøttes i Java.

3. Efterlign udvidelige enumer med grænseflader

Vi har lært, at vi ikke kan oprette en underklasse af et eksisterende enum. Imidlertid er en grænseflade udvidelig. Derfor, vi kan efterligne udvidelige enums ved at implementere en grænseflade.

3.1. Efterligne at udvide konstanterne

For at forstå denne teknik hurtigt, lad os se på, hvordan man kan efterligne udvide vores BasicStringOperation enum at have MD5_ENCODE og BASE64_ENCODE operationer.

Lad os først oprette en interfaceStringOperation:

offentlig grænseflade StringOperation {String getDescription (); } 

Dernæst får vi begge enums til at implementere grænsefladen ovenfor:

offentlig enum BasicStringOperation implementerer StringOperation {TRIM ("Fjernelse af forreste og bageste mellemrum."), TO_UPPER ("Ændring af alle tegn i store bogstaver."), REVERSE ("Vender den givne streng."); privat streng beskrivelse; // constructor and getter override} offentlig enum ExtendedStringOperation implementerer StringOperation {MD5_ENCODE ("Kodning af den givne streng ved hjælp af MD5-algoritmen."), BASE64_ENCODE ("Kodning af den givne streng ved hjælp af BASE64-algoritmen."); privat streng beskrivelse; // konstruktør og getter tilsidesættelse} 

Lad os endelig se på, hvordan man kan efterligne en udvidelig BasicStringOperation enum.

Lad os sige, at vi har en metode i vores ansøgning til at få beskrivelsen af BasicStringOperation enum:

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

Nu kan vi ændre parametertypen BasicStringOperation ind i interfacetypen StringOperation for at få metoden til at acceptere forekomster fra begge enums:

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

3.2. Udvidelse af funktionaliteter

Vi har set, hvordan man kan efterligne udvidede konstanter af enums med grænseflader.

Desuden kan vi også tilføje metoder til grænsefladen for at udvide funktionerne i enums.

For eksempel vil vi udvide vores StringOperation enums, så hver konstant faktisk kan anvende operationen til en given streng:

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

For at opnå det, lad os først tilføje ansøge() metode til grænsefladen:

offentlig grænseflade StringOperation {String getDescription (); String gælder (String input); } 

Derefter lader vi hver StringOperation enum implementere denne metode:

offentlig enum BasicStringOperation implementerer StringOperation {TRIM ("Fjernelse af ledende og bageste mellemrum.") {@Override public String gælder (String input) {return input.trim (); }}, TO_UPPER ("Ændring af alle tegn til store bogstaver.") {@ Override public String gælder (String input) {return input.toUpperCase (); }}, REVERSE ("Vend den givne streng.") {@ Override public String gælder (String input) {returner ny StringBuilder (input). Reverse (). ToString (); }}; // ...} public enum ExtendedStringOperation implementerer StringOperation {MD5_ENCODE ("Kodning af den givne streng ved hjælp af MD5-algoritmen.") {@ Override public String gælder (String input) {return DigestUtils.md5Hex (input); }}, BASE64_ENCODE ("Kodning af den givne streng ved hjælp af BASE64-algoritmen.") {@ Override public String gælder (String input) {return new String (new Base64 (). Encode (input.getBytes ())); }}; // ...} 

En testmetode beviser, at denne fremgangsmåde fungerer som forventet:

@Test offentlig ugyldighed givetAStringAndOperation_whenApplyOperation_thenGetExpectedResult () {String input = "hej"; String expectToUpper = "HELLO"; Streng forventetReverse = "olleh"; String expectTrim = "hej"; Streng forventetBase64 = "IGhlbGxv"; Streng forventetMd5 = "292a5af68d31c10e31ad449bd8f51263"; assertEquals (expectTrim, app.applyOperation (BasicStringOperation.TRIM, input)); assertEquals (expectToUpper, app.applyOperation (BasicStringOperation.TO_UPPER, input)); assertEquals (forventetReverse, app.applyOperation (BasicStringOperation.REVERSE, input)); assertEquals (forventetBase64, app.applyOperation (ExtendedStringOperation.BASE64_ENCODE, input)); assertEquals (forventetMd5, app.applyOperation (ExtendedStringOperation.MD5_ENCODE, input)); } 

4. Udvidelse af en Enum uden at ændre koden

Vi har lært at udvide enum ved at implementere grænseflader.

Imidlertid ønsker vi nogle gange at udvide funktionerne i et enum uden at ændre det. For eksempel vil vi gerne udvide en enum fra et tredjepartsbibliotek.

4.1. Tilknytning af Enum-konstanter og interfaceimplementeringer

Lad os først se på et eksempel på enum:

public enum ImmutableOperation {REMOVE_WHITESPACES, TO_LOWER, INVERT_CASE} 

Lad os sige, at enum er fra et eksternt bibliotek, derfor kan vi ikke ændre koden.

Nu i vores Ansøgning klasse, vi vil have en metode til at anvende den givne operation på inputstrengen:

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

Da vi ikke kan ændre enum-koden, vi kan bruge EnumMap at forbinde enum-konstanterne og de nødvendige implementeringer.

Lad os først oprette en grænseflade:

offentlig grænseflade Operator {String gælder (String input); } 

Derefter opretter vi kortlægningen mellem enum-konstanter og Operatør implementeringer ved hjælp af en EnumMap:

offentlig klasse Anvendelse {privat statisk endelig kort 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åde vores ApplyImmutableOperation () metoden kan anvende den tilsvarende operation på den givne inputstreng:

@Test offentlig ugyldighed givetAStringAndImmutableOperation_whenApplyOperation_thenGetExpectedResult () {String input = "He ll O"; String expectToLower = "han vil være"; 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. Validering af EnumMap Objekt

Nu, hvis enum er fra et eksternt bibliotek, ved vi ikke, om det er blevet ændret eller ej, såsom ved at tilføje nye konstanter til enum. I dette tilfælde, hvis vi ikke ændrer vores initialisering af EnumMap at indeholde den nye enumværdi, vores EnumMap tilgang kan løbe ind i et problem, hvis den nyligt tilføjede enumkonstant videregives til vores applikation.

For at undgå det kan vi validere EnumMap efter initialisering for at kontrollere, om den indeholder 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 kortlagt, hvis (Arrays.stream (ImmutableOperation.values ​​()). AnyMatch (it ->! OPERATION_MAP.containsKey (it))) {kast ny IllegalStateException ("Ikke kortlagt enumkonstant fundet!"); }} 

Som koden ovenfor viser, hvis der er nogen konstant fra Uforanderlig drift er ikke kortlagt, en IllegalStateException vil blive kastet. Da vores validering er i en statisk blok, IllegalStateException vil være årsagen til ExceptionInInitializerError:

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

Således, når applikationen ikke starter med den nævnte fejl og årsag, skal vi dobbelttjekke Uforanderlig drift for at sikre, at alle konstanter er kortlagt.

5. Konklusion

Enum er en speciel datatype i Java. I denne artikel har vi diskuteret, hvorfor enum ikke understøtter arv. Derefter behandlede vi, hvordan man kan efterligne udvidelige enums med grænseflader.

Vi har også lært, hvordan man udvider funktionerne i et enum uden at ændre det.

Som altid er artiklens fulde kildekode tilgængelig på GitHub.


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