当Java 8即将面世时,您确定您对Java 5中引入的枚举很了解吗? Java枚举仍然被低估了,很可惜,因为它们比您想象的要有用,它们不仅仅用于通常的枚举常量!

Java枚举是多态的

Java枚举是可以包含行为甚至数据的真实类。

让我们用一种方法用枚举来代表剪刀石头布游戏。 以下是定义行为的单元测试:

@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();
}

这是枚举的实现,它主要依赖于每个枚举常量的序数整数,例如项N + 1胜过项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;}
}

注意,在任何地方都没有一个IF语句,所有业务逻辑都由整数逻辑和多态性处理,在这里我们将覆盖ROCK情况的方法。 如果项目之间的排序不是循环的,我们可以仅使用枚举的自然排序来实现,这里的多态性有助于处理循环。

您无需任何IF语句就可以做到! 是的你可以!

这个Java枚举也是一个完美的例子,您可以吃下蛋糕(提供带有意图公开名称的漂亮的面向对象的API),也可以吃下蛋糕(使用美好时光的简单而有效的整数逻辑实现)。

在我的上一个项目中,我使用了很多枚举来代替类:它们被保证为单例,具有顺序,哈希码,等值和序列化,并且都是内置的,而源代码中没有任何混乱。

如果您正在寻找Value Objects,并且可以用一组有限的实例代表域的一部分,那么枚举就是您所需要的! 它有点像Scala中的Sealed Case类 ,但是它完全限于在编译时定义的一组实例。 编译时实例的有限集合是一个真正的限制,但是现在有了连续交付功能 ,如果确实需要一种额外的情况,则可以等待下一个版本。  

非常适合策略模式

让我们进入一个(著名的) 欧洲歌唱大赛的系统 ; 我们希望能够配置何时向用户通知(或不通知)任何新的Eurovision事件的行为。 这一点很重要。 让我们用一个枚举来做到这一点:

/** 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);}

并针对非平凡案例进行单元测试: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 ,仅在每种情况下都实现它。 一种替代方法是实现默认行为,并且仅在有意义的每种情况下才覆盖默认行为 ,就像在Rock-Paper-Scissors游戏中一样。

同样,我们不需要打开枚举来选择行为,而是依靠多态。 除了依赖关系之外,您可能不需要太多的枚举。 例如,当枚举是数据传递对象(DTO)中发送给外界的消息的一部分时,您不希望枚举或其签名中的内部代码具有任何依赖性。

对于欧洲电视网的策略,使用TDD我们可以从一个简单的布尔值开始(对于ALWAYS和NEVER)。 一旦我们引入第三个策略ONLY_IF_IN_MY_CITY,它将立即被提升为枚举。 提倡基元也是本着Object Calisthenics第七条规则“ 包装所有基元 ”的精神,而枚举是将布尔值或整数与一组可能的值进行包装的理想方法。

由于策略模式通常是由配置控制的,因此来往String的内置序列化也非常方便存储设置。  

完美匹配国家模式

就像策略模式一样,Java枚举非常适合于有限状态机 ,根据定义,可能状态的集合是有限的。

婴儿作为有限状态机(图片来自www.alongcamebaby.ca)

让我们以简化为状态机的婴儿为例,并使其成为枚举:

/*** 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;}
}

当然,还有一些单元测试可以驱动行为:

@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);
}

是的,我们可以引用它们之间的枚举常量,但前提条件是只能引用以前定义的常量。 在这里,我们在状态EAT-> SLEEP-> POOP-> EAT等之间有一个循环。因此,我们需要打开循环并在运行时使用解决方法将其关闭。

我们确实有一个带有CRY状态的 ,可以从任何状态访问它。

我已经使用枚举通过简单地在每个节点中引用其元素(都带有枚举常量)来按类别表示简单 

枚举优化的集合

枚举还具有为其Map和Set专用实现实现的好处: EnumMapEnumSet

这些集合具有相同的接口,并且行为与常规集合类似,但是在内部,它们将枚举的整数性质用作优化。 简而言之,您将旧的C样式数据结构和习惯用法(位掩码等)隐藏在优雅的界面后面。 这也说明了您不必为了效率而妥协API!

为了说明这些专用集合的用法,让我们代表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张卡,前三张卡更加面向控制,中间的卡平衡,最后三张卡则更加面向委托(我已经解释清楚了,请参阅他的书进行解释)。 在“委托扑克”中,每个玩家都为给定情况选择一张牌,并赚取与该牌价值(从1到7)一样多的积分,“最高少数民族”的玩家除外。

使用顺序值+ 1计算点数很简单。通过顺序值选择面向控制的卡也很简单,或者我们可以像下面所做的那样使用从范围构建的Set来选择面向委托的牌:

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提供了方便的静态工厂方法,例如range(from,to),以创建一个集合,该集合包括在我们的示例中按声明顺序从ADVISE和DELEGATE开始的每个枚举常量。

为了计算最高的少数派,我们从最高的牌开始,除了找到最大值外,别无所求,因为枚举始终是可比的,所以这很琐碎。

每当我们需要将此枚举用作Map中的键时,都应使用EnumMap,如以下测试所示:

// 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枚举:它们在域驱动设计的意义上非常适合值对象,在此意义上限制了所有可能值的集合。 在最近的项目中,我特意设法将大多数值类型表示为枚举。 免费提供许多很棒的功能,尤其是几乎没有技术噪音的情况下。 这有助于提高我在域词和技术术语之间的信噪比

或者当然,我确保每个枚举常量也是不可变的 ,并且免费获取了正确的等于,哈希码,toString,String或整数序列化,单例性和非常有效的集合,所有这些都只需很少的代码即可。

(图片来自sys-con.com – Jim Barnabee文章)»]
多态的力量

枚举多态性非常方便,而且我从不对枚举使用instanceof ,也几乎不需要打开枚举。

我希望Java枚举由类似的构造完成,就像Scala中的case类一样,因为当可能的值集不能被限制时。 强制任何类保持不变的方法也很好。 我问得太多了吗?

同样,<troll>甚至都不要尝试将Java枚举与C#枚举进行比较... </ troll>

参考: Java枚举:您拥有优雅,优雅和力量,这就是我所爱! 从我们的JCG合作伙伴 Cyrille Martraire在Cyrille Martraire的博客博客中获得。

翻译自: https://www.javacodegeeks.com/2012/08/java-enums-you-have-grace-elegance-and.html

Java枚举:您拥有优雅,优雅和力量,这就是我所爱!相关推荐

  1. java枚举和枚举类_Java枚举:您拥有优雅,优雅和力量,这就是我所爱!

    java枚举和枚举类 当Java 8即将面世时,您确定您对Java 5中引入的枚举很熟悉吗? Java枚举仍然被低估了,很可惜,因为它们比您想象的要有用,它们不仅仅用于通常的枚举常量! Java枚举是 ...

  2. Java枚举:小小enum,优雅而干净

    来源:沉默王二(ID:cmower) <Java编程思想>中有这么一句话:"有时恰恰因为它,你才能够'优雅而干净'地解决问题"--这句话说的是谁呢?就是本篇的主角--枚 ...

  3. Java——枚举:优雅而干净的enum

    Java编程思想>中有这么一句话:"有时恰恰因为它,你才能够'优雅而干净'地解决问题"--这句话说的是谁呢?就是本篇的主角--枚举(Enum)--大家鼓掌了. 在之前很长时间 ...

  4. java 设置两个方法互斥_分享两个操作Java枚举的实用方法

    1. 前言 Java枚举在开发中是非常实用的.今天再来分析几个小技巧并且回答一些同学的的疑问.首先要说明的是我的枚举建立在以下的范式之中: 枚举统一接口范式 2. 如何把枚举值绑定的下拉列表 这种场景 ...

  5. java枚举类型的优势在哪里?

    最近在做一个项目,其中涉及到一组操作,命名为: 1. "add"; 2. "logicDel"  3. "physicDel"  4. &q ...

  6. java 枚举 总结,java枚举总结

    java枚举小结 在百度百科上是这样介绍枚举的: 在C#或C++,java等一些计算机编程语言中,枚举类型是一种基本数据类型而不是构造数据类型,而在C语言等计算机编程语言中,它是一种构造数据类型.枚举 ...

  7. Java基础之Java枚举

    絮叨 昨天刚好有遇到一个枚举的小问题,然后发现自己并不是那么熟悉它,然后在开发中,枚举用的特别多,所以有了今天的文章. 什么是枚举 Java中的枚举是一种类型,顾名思义:就是一个一个列举出来.所以它一 ...

  8. 0编译器详解_详解Java枚举类型(Enum)中的方法

    文章前记 程序员工作久了便可能整日忙碌于"增删改查"中,迷失方向,毫无进步. 该公众号致力于分享软件开发相关的原创干货,助你完成从程序员到架构师的进阶之路! 努力!做一个NB的Co ...

  9. JAVA 枚举类的初步理解

    JAVA 枚举类的初步理解 现在Java的枚举类在真实项目中已经用的比较频繁,比静态常量更好用,也更有限定性,enum类可以用来表示有限的类对象,比如星期.月份.性别或者项目中的产品类型 像诸如此类的 ...

最新文章

  1. 我的Chrome常用快捷键
  2. 51nod 1021 石头归并
  3. [简单]poi word2007表格按模版样式填充行数据
  4. mysql 导出数据为csv格式
  5. 2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛 Fishing Master
  6. 第六十七期:全球500强公司的2100万登录信息惊现于暗网上!
  7. 开源当自强:我们不是“便宜货”
  8. git 修改已提交的 commit
  9. prototype、JQuery中跳出each循环的方法
  10. mysql里判断_mysql里如何使用判断语句?
  11. 基于Springboot实现仓库管理系统
  12. 基于Android平台的会议室管理系统详细设计说明书
  13. 一维搜索算法——黄金分割法原理与实现
  14. 实现自己的Protobuf Any
  15. Cesium--模型(modle)的处理篇
  16. Vendor NDK
  17. 卷积层的主要作用_对卷积神经网络CNN的理解,一文读懂卷积神经网络。
  18. 计算数据集的均值、方差
  19. 服务器开机grub gt 命令修复方法,开机grubgt命令修复方法
  20. 广点通-优量汇广告接入文档

热门文章

  1. 10人以下小团队管理手册-学习笔记
  2. selenium自动化测试_维持Selenium测试自动化的完美方法
  3. fork join框架_Java中的Fork / Join框架的简要概述
  4. java jep_Java 10 – JEP 286:局部变量类型推断
  5. activiti 多租户_Activiti中具有独立数据库架构的多租户
  6. spring总结_Spring综合课程总结
  7. 502无法解析服务器标头_编写下载服务器。 第三部分:标头:内容长度和范围...
  8. 在Spring Boot测试中使用Testcontainer进行数据库集成测试
  9. java 代码风格_Java代码风格:最终决定
  10. jax-rs jax-ws_使用JAX-RS的HTTP缓存