Rozšírenie enumov v Jave

1. Prehľad

Typ enum zavedený v prostredí Java 5 je špeciálny údajový typ, ktorý predstavuje skupinu konštánt.

Pomocou enumov môžeme definovať a používať naše konštanty spôsobom bezpečnosti typu. Prináša kontrolu kompilácie konštánt.

Ďalej nám umožňuje používať konštanty v rozvodňa vyhlásenie.

V tomto výučbe sa budeme zaoberať napríklad rozšírením enumov v Jave, pridaním nových konštantných hodnôt a nových funkcií.

2. Enums a dedičstvo

Ak chceme rozšíriť triedu Java, zvyčajne vytvoríme podtriedu. V Jave sú enumy tiež triedy.

V tejto časti sa pozrime, či môžeme zdediť enum ako pri bežných triedach Java.

2.1. Rozšírenie typu Enum

Najskôr sa pozrime na príklad, aby sme problému rýchlo porozumeli:

public enum BasicStringOperation {TRIM ("Odstránenie úvodných a koncových medzier."), TO_UPPER ("Zmena všetkých znakov na veľké písmená."), REVERSE ("Obrátenie daného reťazca."); popis súkromného reťazca; // konštruktor a getter}

Ako ukazuje vyššie uvedený kód, máme enum BasicStringOperation ktorý obsahuje tri základné operácie s reťazcami.

Teraz povedzme, že chceme do enumu pridať nejaké rozšírenie, ako napr MD5_ENCODE a BASE64_ENCODE. Možno prídeme s týmto priamym riešením:

public enum ExtendedStringOperation rozširuje BasicStringOperation {MD5_ENCODE ("Kódovanie daného reťazca pomocou algoritmu MD5."), BASE64_ENCODE ("Kódovanie daného reťazca pomocou algoritmu BASE64."); popis súkromného reťazca; // konštruktor a getter}

Keď sa však pokúsime kompilovať triedu, zobrazí sa chyba kompilátora:

Nie je možné dediť z enum BasicStringOperation

2.2. Pre výčty nie je povolené dedenie

Teraz poďme zistiť, prečo sme dostali našu chybu kompilátora.

Keď kompilujeme enum, kompilátor Java s ním urobí nejaké kúzlo:

  • Urobí z enumu podtriedu abstraktnej triedy java.lang.Enum
  • Zostavuje enum ako a konečné trieda

Napríklad ak rozoberieme náš kompilát BasicStringOperation enum pomocou javap, uvidíme, že je reprezentovaný ako podtrieda java.lang.Enum:

$ javap BasicStringOperation verejná finálna trieda com.baeldung.enums.extendenum.BasicStringOperation rozširuje java.lang.Enum {public static final com.baeldung.enums.extendenum.BasicStringOperation TRIM; verejné statické konečné com.baeldung.enums.extendenum.BasicStringOperation TO_UPPER; verejné statické konečné com.baeldung.enums.extendenum.BasicStringOperation REVERSE; ...} 

Ako vieme, nemôžeme dediť a konečné triedy v Jave. Navyše, aj keby sme mohli vytvoriť ExtendedStringOperation enum dediť BasicStringOperation, náš ExtendedStringOperation enum rozšíri dve triedy: BasicStringOperation a java.lang.Enum. To znamená, že by sa z neho stala situácia s viacnásobným dedičstvom, ktorá v Jave nie je podporovaná.

3. Emulujte rozšíriteľné výčty pomocou rozhraní

Naučili sme sa, že nemôžeme vytvoriť podtriedu existujúceho výčtu. Rozhranie je však rozšíriteľné. Preto môžeme emulovať rozšíriteľné výčty implementáciou rozhrania.

3.1. Emulácia predĺženia konštánt

Aby sme tejto technike rýchlo porozumeli, pozrime sa, ako napodobniť rozšírenie našej BasicStringOperation enum mať MD5_ENCODE a BASE64_ENCODE operácie.

Najprv si vytvorme rozhranieStringOperation:

verejné rozhranie StringOperation {String getDescription (); } 

Ďalej necháme obidve enumy implementovať vyššie uvedené rozhranie:

public enum BasicStringOperation implementuje StringOperation {TRIM ("Odstránenie úvodných a koncových medzier."), TO_UPPER ("Zmena všetkých znakov na veľké písmená."), REVERSE ("Obrátenie daného reťazca."); popis súkromného reťazca; // konštruktor a prepísanie getra} verejné enum ExtendedStringOperation implementuje StringOperation {MD5_ENCODE ("Kódovanie daného reťazca pomocou algoritmu MD5."), BASE64_ENCODE ("Kódovanie daného reťazca pomocou algoritmu BASE64."); popis súkromného reťazca; // prepísanie konštruktora a getra} 

Na záver sa pozrime na to, ako napodobniť rozšíriteľný súbor BasicStringOperation enum.

Povedzme, že máme v našej aplikácii metódu na získanie popisu BasicStringOperation enum:

verejná trieda Aplikácia {public String getOperationDescription (BasicStringOperation stringOperation) {return stringOperation.getDescription (); }} 

Teraz môžeme zmeniť typ parametra BasicStringOperation do typu rozhrania StringOperation aby metóda akceptovala inštancie z oboch enumov:

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

3.2. Rozšírenie funkcií

Videli sme, ako emulovať rozširujúce sa konštanty enumov pomocou rozhraní.

Ďalej môžeme do rozhrania pridať aj metódy na rozšírenie funkčnosti enumov.

Napríklad chceme rozšíriť svoje StringOperation enums, aby každá konštanta mohla skutočne použiť operáciu na daný reťazec:

verejná trieda Aplikácia {public String applyOperation (operácia StringOperation, vstup do reťazca) {return operation.apply (vstup); } // ...} 

Aby sme to dosiahli, najskôr pridajme použiť () metóda na rozhranie:

verejné rozhranie StringOperation {String getDescription (); Použiť reťazec (vstup reťazca); } 

Ďalej sme každého nechali StringOperation enum implementovať túto metódu:

public enum BasicStringOperation implementuje StringOperation {TRIM ("Odstránenie úvodných a koncových medzier.") {@Override public String apply (vstup do reťazca) {return input.trim (); }}, TO_UPPER ("Zmena všetkých znakov na veľké písmená.") {@Override public String apply (reťazcový vstup) {návrat input.toUpperCase (); }}, REVERSE ("Obrátenie daného reťazca.") {@Override public String apply (String input) {return new StringBuilder (input) .reverse (). ToString (); }}; // ...} public enum ExtendedStringOperation implementuje StringOperation {MD5_ENCODE ("Kódovanie daného reťazca pomocou algoritmu MD5.") {@Override public String apply (vstup do reťazca) {return DigestUtils.md5Hex (vstup); }}, BASE64_ENCODE ("Kódovanie daného reťazca pomocou algoritmu BASE64.") {@Override public String apply (String input) {return new String (new Base64 (). Encode (input.getBytes ())); }}; // ...} 

Testovacia metóda dokazuje, že tento prístup funguje podľa našich očakávaní:

@Test public void givenAStringAndOperation_whenApplyOperation_thenGetExpectedResult () {reťazec input = "ahoj"; Reťazec expectToUpper = "AHOJ"; Reťazec expectReverse = "olleh"; Reťazec expectTrim = "ahoj"; Reťazec expectBase64 = "IGhlbGxv"; Reťazec expectMd5 = "292a5af68d31c10e31ad449bd8f51263"; assertEquals (expectTrim, app.applyOperation (BasicStringOperation.TRIM, vstup)); assertEquals (expectToUpper, app.applyOperation (BasicStringOperation.TO_UPPER, vstup)); assertEquals (expectReverse, app.applyOperation (BasicStringOperation.REVERSE, vstup)); assertEquals (expectBase64, app.applyOperation (ExtendedStringOperation.BASE64_ENCODE, vstup)); assertEquals (expectMd5, app.applyOperation (ExtendedStringOperation.MD5_ENCODE, vstup)); } 

4. Rozšírenie výčtu bez zmeny kódu

Naučili sme sa, ako rozšíriť enum implementáciou rozhraní.

Niekedy však chceme rozšíriť funkčnosť enumu bez jeho úpravy. Napríklad by sme chceli rozšíriť výčet z knižnice tretej strany.

4.1. Priradenie konštánt výčtu a implementácie rozhrania

Najprv sa pozrime na príklad enum:

verejné enum ImmutableOperation {REMOVE_WHITESPACES, TO_LOWER, INVERT_CASE} 

Povedzme, že enum je z externej knižnice, preto nemôžeme zmeniť kód.

Teraz v našom Aplikácia triedy, chceme mať metódu na aplikovanie danej operácie na vstupný reťazec:

public String applyImmutableOperation (operácia ImmutableOperation, vstup do reťazca) {...}

Pretože nemôžeme zmeniť enum kód, môžeme použiť EnumMap priradiť konštanty enum a požadované implementácie.

Najskôr vytvorme rozhranie:

public interface Operator {String apply (String input); } 

Ďalej vytvoríme mapovanie medzi konštantami enum a Prevádzkovateľ implementácie pomocou EnumMap:

verejná trieda Aplikácia {private static final Mapa OPERATION_MAP; static {OPERATION_MAP = nový 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, vstup -> vstup.replaceAll ("\ s", "")); } public String applyImmutableOperation (operácia ImmutableOperation, vstup do reťazca) {return operationMap.get (operation) .apply (input); }

Týmto spôsobom naše applyImmutableOperation () metóda môže použiť príslušnú operáciu na daný vstupný reťazec:

@Test public void givenAStringAndImmutableOperation_whenApplyOperation_thenGetExpectedResult () {String input = "He ll O"; Reťazec expectToLower = "he ll o"; Reťazec expectRmWhitespace = "HellO"; Reťazec expectInvertCase = "hE LL o"; assertEquals (expectToLower, app.applyImmutableOperation (ImmutableOperation.TO_LOWER, vstup)); assertEquals (expectRmWhitespace, app.applyImmutableOperation (ImmutableOperation.REMOVE_WHITESPACES, vstup)); assertEquals (expectInvertCase, app.applyImmutableOperation (ImmutableOperation.INVERT_CASE, vstup)); } 

4.2. Validácia EnumMap Objekt

Teraz, ak je enum z externej knižnice, nevieme, či bol zmenený alebo nie, napríklad pridaním nových konštánt do enumu. V takom prípade, ak nezmeníme svoju inicializáciu EnumMap aby obsahoval novú enum hodnotu, našu EnumMap prístup môže naraziť na problém, ak sa do našej aplikácie predá novo pridaná konštanta enum.

Aby sme sa tomu vyhli, môžeme overiť EnumMap po jeho inicializácii skontrolovať, či obsahuje všetky konštanty výčtu:

static {OPERATION_MAP = nový EnumMap (ImmutableOperation.class); OPERATION_MAP.put (ImmutableOperation.TO_LOWER, String :: toLowerCase); OPERATION_MAP.put (ImmutableOperation.INVERT_CASE, StringUtils :: swapCase); // ImmutableOperation.REMOVE_WHITESPACES nie je namapovaný if (Arrays.stream (ImmutableOperation.values ​​()). AnyMatch (it ->! OPERATION_MAP.containsKey (it))) {throw new IllegalStateException ("Unmapped enum constant found!"); }} 

Ako ukazuje vyššie uvedený kód, ak existuje, konštanta z ImmutableOperation nie je mapovaný, an IllegalStateException bude vyhodený. Keďže naša validácia je v statický blok, IllegalStateException bude príčinou ExceptionInInitializerError:

@Test public void givenUnmappedImmutableOperationValue_whenAppStarts_thenGetException () {Throwable throwable = assertThrows (ExceptionInInitializerError.class, () -> {ApplicationWithEx appEx = new ApplicationWithEx ();}); assertTrue (throwable.getCause () instanceof IllegalStateException); } 

Akonáhle sa teda aplikácia nepodarí spustiť so spomínanou chybou a príčinou, mali by sme dvakrát skontrolovať ImmutableOperation aby ste sa ubezpečili, že sú mapované všetky konštanty.

5. Záver

Enum je špeciálny dátový typ v jazyku Java. V tomto článku sme diskutovali o tom, prečo enum nepodporuje dedičnosť. Potom sme sa zaoberali tým, ako emulovať rozšíriteľné enumy pomocou rozhraní.

Tiež sme sa naučili, ako rozšíriť funkčnosť enumu bez zmeny.

Celý zdrojový kód článku je ako vždy k dispozícii na GitHub.


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