扩展方法 枚举值

前言

在上一篇文章中,我解释了如何以及为什么在Java代码中使用enums而不是switch/case控制结构。 在这里,我将展示如何扩展现有enums功能。

介绍

Java enum是一种编译器魔术。 在字节码中,任何enum都表示为扩展抽象类java.lang.Enum并具有几个静态成员的类。 因此,枚举不能扩展任何其他类或枚举:没有多重继承。

类也不能扩展枚举。 此限制由编译器强制执行。

这是一个简单的enum

 enum Color {red, green, blue} 

此类尝试扩展它:

 SubColor class extends Color {} 

这是尝试编译类SubColor的结果:

 $ javac SubColor.java  SubColor.java: 1 : error: cannot inherit from final Color  SubColor class extends Color {} ^  SubColor.java: 1 : error: enum types are not extensible  SubColor class extends Color {}  ^  2 errors 

Enum既不能扩展也不能扩展。 那么,如何扩展其功能呢? 关键字是“功能”。 Enum可以实现方法。 例如,枚举Color可以声明抽象方法draw() ,每个成员都可以重写它:

enum Color {red { @Override public void draw() { } },green { @Override public void draw() { } },blue { @Override public void draw() { } },;public abstract void draw();
}
在此说明该技术的流行用法。 不幸的是,不可能总是在枚举本身中实现方法,因为:
  1. 枚举可能属于第三方库或公司中的其他团队
  2. 枚举可能被过多的其他数据和函数重载,因此变得不可读
  3. 枚举属于不具有实现方法draw()所需的依赖项的模块。

本文针对此问题提出了以下解决方案。

镜像枚举

我们不能修改枚举颜色吗? 没问题! 让我们创建具有与Color完全相同的元素的枚举DrawableColor。 这个新的枚举将实现我们的方法draw():
enum DrawableColor {red { @Override public void draw() { } },green { @Override public void draw() { } },blue { @Override public void draw() { } },;public abstract void draw();
}

这个枚举是源枚举Color的一种反映,即Color是它的镜像 。但是如何使用新的枚举呢? 我们所有的代码都使用Color ,而不是DrawableColor 。 实现此过渡的最简单方法是使用内置的枚举方法name()和valueOf(),如下所示:

Color color = ...
DrawableColor.valueOf(color.name()).draw();

由于name()方法是最终方法,不能被覆盖,并且valueOf()由编译器生成,因此这些方法始终相互配合,因此在此不会出现功能问题。 这种过渡的性能也很好:方法name()甚至不创建新的String而是返回预先初始化的字符串(请参见java.lang.Enum源代码)。 方法valueOf()是使用Map实现的,因此其复杂度为O(1)。

上面的代码包含明显的问题。 如果更改了源枚举Color,则辅助枚举DrawableColor不知道这一事实,因此使用name()valueOf()的技巧在运行时将失败。 我们不希望这种情况发生。 但是如何防止可能的故障呢?

我们必须让DrawableColor知道它的镜像是Color,并且最好在编译时或至少在单元测试阶段强制执行它。 在这里,我们建议在单元测试执行期间进行验证。 Enum可以实现时所执行的静态初始化enum中的任何代码被提及。 这实际上意味着,如果静态初始化程序验证枚举DrawableColor是否适合Color,则足以执行以下测试,以确保代码不会在生产环境中被破坏:

@Test
public void drawableColorFitsMirror {DrawableColor.values();
}

静态初始化程序只需要比较DrawableColorColor元素,如果不匹配则抛出异常。 该代码很简单,可以针对每种特殊情况编写。 幸运的是,名为enumus的简单开放源代码库已经实现了此功能,因此该任务变得微不足道:

enum DrawableColor {....static {Mirror.of(Color.class);}
}

而已。 如果源枚举和DrawableColor不再合适,则测试将失败。 实用程序类Mirror具有获取2个参数的其他方法:必须适合2个枚举的类。 可以从代码中的任何位置调用此版本,而不仅仅是从必须经过验证的枚举中调用。

枚举地图

我们是否真的必须定义仅包含一种方法的实现的另一个枚举? 实际上,我们不必这样做。 这是一个替代解决方案。 让我们定义接口抽屉如下:

public interface Drawer {void draw();
}

现在,让我们在枚举元素和接口Drawer的实现之间创建映射:

Map<Color, Drawer> drawers = new EnumMap<>(Color.class) {{put(red, new Drawer() { @Override public void draw();});put(green, new Drawer() { @Override public void draw();})put(blue, new Drawer() { @Override public void draw();})
}}

用法很简单:

 drawers.get(color).draw(); 

这里选择EnumMap作为Map实现,以获得更好的性能。 Map保证每个枚举元素仅出现一次。 但是,它不能保证每个enum元素都有相应的条目。 但是检查映射的大小等于enum元素的数量就足够了:

 drawers.size() == Color.values().length 

枚举还建议在这种情况下方便实用。 如果地图不适合Color,则以下代码将引发IllegalStateException及其描述性消息:

 EnumMapValidator.validateValues(Color. class , map, "Colors map" ); 

从单元测试执行的代码中调用验证器很重要。 在这种情况下,基于地图的解决方案对于将来对源枚举的修改是安全的。

EnumMap和Java 8功能接口

实际上,我们不必定义特殊的接口来扩展
枚举功能。 从版本8开始,我们可以使用JDK提供的功能接口之一( Function,BiFunction,Consumer,BiConsumer,
Supplieretc
Function,BiFunction,Consumer,BiConsumer,
Supplieretc
Function,BiFunction,Consumer,BiConsumer,
Supplieretc
。)选择取决于必须发送给功能的参数。 例如,可以使用Supplier代替上一个示例中定义的Drawable

 Map<Color, Supplier<Void>> drawers = new EnumMap<>(Color. class ) {{ put(red, new Supplier<Void>() { @Override public void get();}); put(green, new Supplier<Void>() { @Override public void get();}) put(blue, new Supplier<Void>() { @Override public void get();})  }} 

该映射的用法与上一个示例非常相似:

 drawers.get(color).get(); 

该地图可以与存储以下实例的地图完全一样地进行验证:
可绘制。

结论

本文说明了如果我们在其中添加一些逻辑,那么Java enums有多么强大。 它还演示了两种扩展语言enums功能的方法,尽管存在语言限制。 本文向用户介绍了名为enumus的开源库,该库提供了几个有用的实用工具,这些工具有助于简化 enums操作。

翻译自: https://www.javacodegeeks.com/2019/03/two-ways-extend-enum-functionality.html

扩展方法 枚举值

扩展方法 枚举值_扩展枚举功能的两种方法相关推荐

  1. xp系统的计算机管理中用户在哪里,计算机管理在哪里_打开XP系统计算机管理功能的两种方法...

    摘要 腾兴网为您分享:打开XP系统计算机管理功能的两种方法,周公解梦,招联金融,愈加,晓黑板等软件知识,以及安徽省12366,向日葵控制端和,刷火车票软件,云创通手机,faceit,美莱尔塑胶地板,杭 ...

  2. win10关闭“Windows安全中心”功能的两种方法

    win10系统怎么将windows安全中心关闭? 听语音 原创 | 浏览:10407 | 更新:2020-03-24 10:54 1 2 3 4 5 6 7 分步阅读 一些软件需要将Windows安全 ...

  3. vue实现打印功能的两种方法/web打印控件

    第一种方法:通过npm 安装插件 1,安装  npm install vue-print-nb --save 2,引入  安装好以后在main.js文件中引入 1 2 import Print fro ...

  4. 水平集方法引入主动轮廓模型算法中的两种方法

    水平集方法引入主动轮廓模型算法中的两种方法 1.传统的基于主动轮廓模型和水平集理论的方法 2.变分水平集方法 在讲解水平集理论在主动轮廓模型中的应用前,我们先用流程图说明一下常见的处理主动轮廓模型的流 ...

  5. 两种解决Qt5显示中文乱码的方法(使用QStringLiteral和#pragma execution_character_set(utf-8)两种方法)

    两种解决Qt5显示中文乱码的方法(使用QStringLiteral和#pragma execution_character_set("utf-8")两种方法) 升级到Qt5.X之后 ...

  6. Django 后台admin管理页面添加简易导出/下载数据功能的两种方法

    在Django中有时候需要在后台界面需要用到导出数据的功能,对于功能要求比较高的可以直接使用Import_Export库来完成.但是对于一些简单的数据导出,可以直接自定义一些简单的函数来实现导出数据. ...

  7. cad添加自己线性_创建cad线型的两种方法(线型文件和linetype) - CAD自学网

    作图过程中,我们最常见的线型是实线.虚线.点划线.有的时候这些基本线型可能满足不了你的需求.CAD也有自带的特殊线型,比如-HW-,这种自带文字的线型.但你想要的确实-X-.那么这便涉及到新线型的建立 ...

  8. Python中按值来获取指定键的两种方法

    Python字典中的键是唯一的,但不同的键可以对应同样的值,比如说uid,可以是1001.id同样可以是1001.这样的话通过值来获取指定的键,就不止一个!而且也并不太好处理.这里同样提供两种思路来处 ...

  9. Win7开启休眠功能的两种方法(步骤)

    win7的休眠功能给我们带来了许多的方便,当我们需要离开电脑一段时间的时候,可以使用休眠功能进行当前工作状态的保存维持,方便下次再次使用. 工具/原料 电脑 win7 方法/步骤1 1 第一种方法 首 ...

  10. 在两个高程点之间如何生成加密点_地形图加密高程点的两种方法CAD和ARCGIS

    地形图加密高程点的两种方法CAD和ARCGIS 1.      CAD中步骤如下 1)  在cad中加载高程点数据信息,然后"工程应用"-"高程点生成数据文件" ...

最新文章

  1. SAP MM ML81N为采购订单创建服务接收单,报错- No matching PO items selected -
  2. SQL Server 2005 Express 版
  3. Kolla 让 OpenStack 部署更贴心
  4. lay和lied_lie和lay的区别和用法是什么
  5. 鸿蒙os2.0游戏体验,华为运行鸿蒙OS 2.0体验:界面近似EMUI 11
  6. 时间序列分析源资料汇总
  7. java自动生成类_自动生成优化的Java类专业知识
  8. Nginx(一):概念基础
  9. sql 两表数据合并_多表查询SQL语句
  10. Mac做深度学习开发【从无到有】
  11. sqlmap安装和使用
  12. Skyline软件二次开发初级——6如何在WEB页面中的三维地图上进行坐标和方向计算...
  13. epub电子书--目录结构介绍
  14. C语言:字符串转为long long --- atoll函数
  15. HyperLynx(二十)DDR(三)DIMM、DD2、DDR3、DDR4和DDR5介绍
  16. Excel怎么快速选中相同背景颜色单元格
  17. React的调和过程(Reconciliation)
  18. kdj买卖指标公式源码_精品 玩转KDJ【精准买卖提示、源码、副图、说明】
  19. 如何进行航拍全景摄影(下)
  20. FFmpeg+SDL纯语音播放器

热门文章

  1. 2020牛客暑期多校训练营(第六场)
  2. 【每日一题】7月7日题目精讲—最短路
  3. [ZJOI2016]旅行者(网格图分治最短路)
  4. [TJOI2013]拯救小矮人(反悔贪心证明),「ICPC World Finals 2019」Hobson 的火车(基环树,差分)
  5. P4428-[BJOI2018]二进制【树状数组,set】
  6. P3703-[SDOI2017]树点涂色【LCT,线段树】
  7. P1455-搭配购买【图论,并查集,dp,背包】
  8. HDU 5008 Boring String Problem ( 后缀数组求本质不同第k大子串)
  9. [XSY3343] 程序锁(DP)
  10. HDU5874 - Friends and Enemies