java枚举和枚举类_Java枚举:您拥有优雅,优雅和力量,这就是我所爱!
java枚舉和枚舉類(lèi)
當(dāng)Java 8即將面世時(shí),您確定您對(duì)Java 5中引入的枚舉很熟悉嗎? Java枚舉仍然被低估了,很可惜,因?yàn)樗鼈儽饶胂蟮囊杏?#xff0c;它們不僅僅用于通常的枚舉常量!Java枚舉是多態(tài)的
Java枚舉是可以包含行為甚至數(shù)據(jù)的真實(shí)類(lèi)。
讓我們用一種方法用枚舉來(lái)表示“ 剪刀石頭布”游戲。 以下是定義行為的單元測(cè)試:
@Test public void paper_beats_rock() {assertThat(PAPER.beats(ROCK)).isTrue();assertThat(ROCK.beats(PAPER)).isFalse(); } @Test public void scissors_beats_paper() {assertThat(SCISSORS.beats(PAPER)).isTrue();assertThat(PAPER.beats(SCISSORS)).isFalse(); } @Test public void rock_beats_scissors() {assertThat(ROCK.beats(SCISSORS)).isTrue();assertThat(SCISSORS.beats(ROCK)).isFalse(); }這是枚舉的實(shí)現(xiàn),它主要依賴(lài)于每個(gè)枚舉常量的序數(shù)整數(shù),例如項(xiàng)N + 1勝過(guò)項(xiàng)N。在許多情況下,枚舉常量和整數(shù)之間的等效關(guān)系非常方便。
/** Enums have behavior! */ public enum Gesture {ROCK() {// Enums are polymorphic, that's really handy!@Overridepublic boolean beats(Gesture other) {return other == SCISSORS;}},PAPER, SCISSORS;// we can implement with the integer representationpublic boolean beats(Gesture other) {return ordinal() - other.ordinal() == 1;} }請(qǐng)注意,在任何地方都沒(méi)有一個(gè)IF語(yǔ)句,所有業(yè)務(wù)邏輯都是由整數(shù)邏輯和多態(tài)處理的,其中我們覆蓋了ROCK情況的方法。 如果項(xiàng)目之間的排序不是循環(huán)的,我們可以?xún)H使用枚舉的自然排序來(lái)實(shí)現(xiàn),這里的多態(tài)性有助于處理循環(huán)。
| 您無(wú)需任何IF語(yǔ)句就可以做到! 是的你可以! |
這個(gè)Java枚舉也是一個(gè)完美的例子,您可以吃下蛋糕(提供帶有意圖公開(kāi)名稱(chēng)的漂亮的面向?qū)ο蟮腁PI),也可以吃下蛋糕(使用美好時(shí)光的簡(jiǎn)單而有效的整數(shù)邏輯實(shí)現(xiàn))。
在我的上一個(gè)項(xiàng)目中,我使用了很多枚舉來(lái)代替類(lèi):它們被保證為單例,具有順序,哈希碼,等價(jià)物和序列化功能,并且它們都內(nèi)置于文本中,而源代碼中沒(méi)有任何混亂。
如果您正在尋找Value Objects,并且可以用一組有限的實(shí)例代表域的一部分,那么枚舉就是您所需要的! 它有點(diǎn)像Scala中的Sealed Case類(lèi) ,但是它完全限于在編譯時(shí)定義的一組實(shí)例。 編譯時(shí)實(shí)例的有限集合是一個(gè)真正的限制,但是現(xiàn)在有了連續(xù)交付功能 ,如果確實(shí)需要一種額外的情況,則可以等待下一個(gè)版本。 ?
非常適合策略模式
讓我們進(jìn)入一個(gè)(著名的) 歐洲歌唱大賽的系統(tǒng) ; 我們希望能夠配置何時(shí)向用戶(hù)通知(或不通知)任何新的Eurovision事件的行為。 這一點(diǎn)很重要。 讓我們用一個(gè)枚舉來(lái)做到這一點(diǎn):
/** The policy on how to notify the user of any Eurovision song contest event */ public enum EurovisionNotification {/** I love Eurovision, don't want to miss it, never! */ALWAYS() {@Overridepublic boolean mustNotify(String eventCity, String userCity) {return true;}},/*** I only want to know about Eurovision if it takes place in my city, so* that I can take holidays elsewhere at the same time*/ONLY_IF_IN_MY_CITY() {// a case of flyweight pattern since we pass all the extrinsi data as// arguments instead of storing them as member data@Overridepublic boolean mustNotify(String eventCity, String userCity) {return eventCity.equalsIgnoreCase(userCity);}},/** I don't care, I don't want to know */NEVER() {@Overridepublic boolean mustNotify(String eventCity, String userCity) {return false;}};// no default behaviorpublic abstract boolean mustNotify(String eventCity, String userCity);}以及針對(duì)非平凡案例的單元測(cè)試ONLY_IF_IN_MY_CITY:
@Test public void notify_users_in_Baku_only() {assertThat(ONLY_IF_IN_MY_CITY.mustNotify("Baku", "BAKU")).isTrue();assertThat(ONLY_IF_IN_MY_CITY.mustNotify("Baku", Paris")).isFalse(); }在這里,我們定義方法abstract ,僅針對(duì)每種情況實(shí)施該方法。 一種替代方法是實(shí)現(xiàn)默認(rèn)行為,并且僅在有意義的每種情況下才覆蓋它,就像在Rock-Paper-Scissors游戲中一樣。
同樣,我們不需要打開(kāi)枚舉來(lái)選擇行為,而是依靠多態(tài)。 除了依賴(lài)關(guān)系之外,您可能不需要太多的枚舉。 例如,當(dāng)枚舉是數(shù)據(jù)傳遞對(duì)象(DTO)中發(fā)送給外界的消息的一部分時(shí),您不希望枚舉對(duì)其簽名中的內(nèi)部代碼有任何依賴(lài)性。
對(duì)于歐洲電視網(wǎng)的策略,使用TDD我們可以從一個(gè)簡(jiǎn)單的布爾值開(kāi)始(對(duì)于ALWAYS和NEVER)。 一旦我們引入第三個(gè)策略O(shè)NLY_IF_IN_MY_CITY,它將立即被提升為枚舉。 提倡基元也是本著Object calisthenics第七條規(guī)則“ 包裝所有基元 ”的精神,而枚舉是包裝布爾值或整數(shù)以及一組可能值的理想方法。
由于策略模式通常是由配置控制的,因此往返于String的內(nèi)置序列化也非常方便存儲(chǔ)您的設(shè)置。 ?
完美匹配國(guó)家模式
就像策略模式一樣,Java枚舉非常適合于有限狀態(tài)機(jī) ,根據(jù)定義,可能狀態(tài)的集合是有限的。
| 嬰兒作為有限狀態(tài)機(jī)(圖片來(lái)自www.alongcamebaby.ca) |
讓我們以簡(jiǎn)化為狀態(tài)機(jī)的嬰兒為例,并使其成為枚舉:
/*** The primary baby states (simplified)*/ public enum BabyState {POOP(null), SLEEP(POOP), EAT(SLEEP), CRY(EAT);private final BabyState next;private BabyState(BabyState next) {this.next = next;}public BabyState next(boolean discomfort) {if (discomfort) {return CRY;}return next == null ? EAT : next;} }當(dāng)然,還有一些單元測(cè)試可以驅(qū)動(dòng)行為:
@Test public void eat_then_sleep_then_poop_and_repeat() {assertThat(EAT.next(NO_DISCOMFORT)).isEqualTo(SLEEP);assertThat(SLEEP.next(NO_DISCOMFORT)).isEqualTo(POOP);assertThat(POOP.next(NO_DISCOMFORT)).isEqualTo(EAT); }@Test public void if_discomfort_then_cry_then_eat() {assertThat(SLEEP.next(DISCOMFORT)).isEqualTo(CRY);assertThat(CRY.next(NO_DISCOMFORT)).isEqualTo(EAT); }是的,我們可以引用它們之間的枚舉常量,但前提條件是只能引用之前定義的常量。 在這里,我們?cè)跔顟B(tài)EAT->睡眠-> POOP-> EAT等之間有一個(gè)循環(huán)。因此,我們需要在運(yùn)行時(shí)打開(kāi)循環(huán)并使用變通辦法將其關(guān)閉。
我們確實(shí)有一個(gè)帶有CRY狀態(tài)的圖 ,可以從任何狀態(tài)訪(fǎng)問(wèn)它。
我已經(jīng)使用枚舉通過(guò)簡(jiǎn)單地在每個(gè)節(jié)點(diǎn)中引用其元素(都帶有枚舉常量)來(lái)按類(lèi)別表示簡(jiǎn)單樹(shù) 。 ?
枚舉優(yōu)化的集合
枚舉還具有針對(duì)Map和Set的專(zhuān)用實(shí)現(xiàn)的優(yōu)點(diǎn): EnumMap和EnumSet 。
這些集合具有相同的接口,并且行為與常規(guī)集合類(lèi)似,但是在內(nèi)部,它們將枚舉的整數(shù)性質(zhì)用作優(yōu)化。 簡(jiǎn)而言之,您將舊的C樣式數(shù)據(jù)結(jié)構(gòu)和習(xí)慣用法(位掩碼等)隱藏在優(yōu)雅的界面后面。 這也說(shuō)明了您不必為了效率而妥協(xié)API!
為了說(shuō)明這些專(zhuān)用集合的用法,讓我們代表Jurgen Appelo的委派撲克中的7張牌:
public enum AuthorityLevel {/** make decision as the manager */TELL,/** convince people about decision */SELL,/** get input from team before decision */CONSULT,/** make decision together with team */AGREE,/** influence decision made by the team */ADVISE,/** ask feedback after decision by team */INQUIRE,/** no influence, let team work it out */DELEGATE;一共有7張卡,前三張卡更加面向控制,中間的卡平衡,最后三張卡更加面向委托(我做了解釋,請(qǐng)參閱他的書(shū)以獲取解釋)。 在“委托撲克”中,每個(gè)玩家都為特定情況選擇一張牌,并獲得與該牌價(jià)值(從1到7)一樣多的積分,“最高少數(shù)民族”的玩家除外。
使用序數(shù)值+ 1計(jì)算點(diǎn)數(shù)很簡(jiǎn)單。也可以通過(guò)序數(shù)值選擇面向控制的卡,也很簡(jiǎn)單,或者我們可以像下面所做的那樣使用從范圍構(gòu)建的Set來(lái)選擇面向委托的牌:
public int numberOfPoints() {return ordinal() + 1;}// It's ok to use the internal ordinal integer for the implementationpublic boolean isControlOriented() {return ordinal() < AGREE.ordinal();}// EnumSet is a Set implementation that benefits from the integer-like// nature of the enumspublic static Set DELEGATION_LEVELS = EnumSet.range(ADVISE, DELEGATE);// enums are comparable hence the usual benefitspublic static AuthorityLevel highest(List levels) {return Collections.max(levels);} }EnumSet提供了方便的靜態(tài)工廠(chǎng)方法,例如range(from,to),以創(chuàng)建一個(gè)集合,該集合包括在我們的示例中按聲明順序從ADVISE和DELEGATE開(kāi)始的每個(gè)枚舉常量。
為了計(jì)算最高的少數(shù)派,我們從最高的牌開(kāi)始,除了找到最大值外,別無(wú)所求,因?yàn)槊杜e始終是可比的,所以這是微不足道的。
每當(dāng)我們需要將此枚舉用作Map中的鍵時(shí),都應(yīng)使用EnumMap,如以下測(cè)試所示:
// Using an EnumMap to represent the votes by authority level @Test public void votes_with_a_clear_majority() {final Map<AuthorityLevel, Integer> votes = new EnumMap(AuthorityLevel.class);votes.put(SELL, 1);votes.put(ADVISE, 3);votes.put(INQUIRE, 2);assertThat(votes.get(ADVISE)).isEqualTo(3); }Java枚舉很好,吃掉它們!
我喜歡Java枚舉:它們?cè)谟蝌?qū)動(dòng)設(shè)計(jì)的意義上非常適合于值對(duì)象,在這種情況下,所有可能值的集合都受到限制。 在最近的項(xiàng)目中,我特意設(shè)法將大多數(shù)值類(lèi)型表示為枚舉。 免費(fèi)提供許多很棒的功能,尤其是幾乎沒(méi)有技術(shù)噪音的情況下。 這有助于提高我在域詞和技術(shù)術(shù)語(yǔ)之間的信噪比 。
或者當(dāng)然,我確保每個(gè)枚舉常量也是不可變的 ,并且免費(fèi)獲取了正確的等于,哈希碼,toString,String或整數(shù)序列化,單例性和非常有效的集合,所有這些只需很少的代碼。
(圖片來(lái)自sys-con.com – Jim Barnabee文章)?]| 多態(tài)的力量 |
枚舉多態(tài)性非常方便,而且我從不對(duì)枚舉使用instanceof ,也幾乎不需要打開(kāi)枚舉。
我希望Java枚舉由類(lèi)似的構(gòu)造完成,就像Scala中的case類(lèi)一樣,因?yàn)楫?dāng)可能的值集不能被限制時(shí)。 強(qiáng)制任何類(lèi)保持不變的方法也很好。 我問(wèn)得太多了嗎?
同樣,<troll>甚至不要嘗試將Java枚舉與C#枚舉進(jìn)行比較... </ troll>
參考: Java枚舉:您擁有優(yōu)雅,優(yōu)雅和力量,這就是我所愛(ài)! 從我們的JCG合作伙伴 Cyrille Martraire在Cyrille Martraire的博客博客中獲得。
翻譯自: https://www.javacodegeeks.com/2012/08/java-enums-you-have-grace-elegance-and.html
java枚舉和枚舉類(lèi)
總結(jié)
以上是生活随笔為你收集整理的java枚举和枚举类_Java枚举:您拥有优雅,优雅和力量,这就是我所爱!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: winxp系统如何运行桌面清理向导
- 下一篇: 三星E3屏幕(三星e3屏幕怎么样)