扩展枚举功能的两种方法
前言
在上一篇文章中,我解释了如何以及为什么在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();
}
- 枚举可能属于第三方库或公司中的其他团队
- 枚举可能被过多的其他数据和函数重载,因此变得不可读
- 枚举属于模块,该模块不具有实现方法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而是返回预初始化的String(请参见java.lang.Enum
源代码)。 方法valueOf()
是使用Map实现的,因此其复杂度为O(1)。
上面的代码包含明显的问题。 如果更改了源枚举Color,则辅助枚举DrawableColor将不知道这一事实,因此具有name()
和valueOf()
的技巧将在运行时失败。 我们不希望这种情况发生。 但是如何防止可能的故障?
我们必须让DrawableColor
知道其镜像是Color,并且最好在编译时或至少在单元测试阶段强制执行此操作。 在这里,我们建议在单元测试执行期间进行验证。 Enum
可以实现时所执行的静态初始化enum
中的任何代码被提及。 这实际上意味着,如果静态初始化程序验证枚举DrawableColor是否适合Color,则足以执行以下测试,以确保代码不会在生产环境中被破坏:
@Test
public void drawableColorFitsMirror {DrawableColor.values();
}
静态初始化器只需要比较DrawableColor
和Color
元素,如果不匹配则抛出异常。 该代码很简单,可以针对每种特定情况编写。 幸运的是,名为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,
SupplieretcFunction,BiFunction,Consumer,BiConsumer,
SupplieretcFunction,BiFunction,Consumer,BiConsumer,
。)选择取决于必须发送给功能的参数。 例如,可以使用
SupplieretcSupplier
代替上一个示例中定义的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
扩展枚举功能的两种方法相关推荐
- 扩展方法 枚举值_扩展枚举功能的两种方法
扩展方法 枚举值 前言 在上一篇文章中,我解释了如何以及为什么在Java代码中使用enums而不是switch/case控制结构. 在这里,我将展示如何扩展现有enums功能. 介绍 Java enu ...
- win10关闭“Windows安全中心”功能的两种方法
win10系统怎么将windows安全中心关闭? 听语音 原创 | 浏览:10407 | 更新:2020-03-24 10:54 1 2 3 4 5 6 7 分步阅读 一些软件需要将Windows安全 ...
- vue实现打印功能的两种方法/web打印控件
第一种方法:通过npm 安装插件 1,安装 npm install vue-print-nb --save 2,引入 安装好以后在main.js文件中引入 1 2 import Print fro ...
- xp系统的计算机管理中用户在哪里,计算机管理在哪里_打开XP系统计算机管理功能的两种方法...
摘要 腾兴网为您分享:打开XP系统计算机管理功能的两种方法,周公解梦,招联金融,愈加,晓黑板等软件知识,以及安徽省12366,向日葵控制端和,刷火车票软件,云创通手机,faceit,美莱尔塑胶地板,杭 ...
- linux+swap分区规则_扩展Linux swap分区 两种方法
先来查询一下系统的swap [root@localhost ~]# free -m total used free shared buffers cached Mem: 375 369 6 0 7 8 ...
- Django 后台admin管理页面添加简易导出/下载数据功能的两种方法
在Django中有时候需要在后台界面需要用到导出数据的功能,对于功能要求比较高的可以直接使用Import_Export库来完成.但是对于一些简单的数据导出,可以直接自定义一些简单的函数来实现导出数据. ...
- Win7开启休眠功能的两种方法(步骤)
win7的休眠功能给我们带来了许多的方便,当我们需要离开电脑一段时间的时候,可以使用休眠功能进行当前工作状态的保存维持,方便下次再次使用. 工具/原料 电脑 win7 方法/步骤1 1 第一种方法 首 ...
- 普中51pwm_51单片机实现PWM输出功能的两种方法解析
51单片机没有PWM输出功能,可以采用定时器配合软件的方法实现,对精度要求不高的场合是非常实用的.采用高速光隔6N137输出,并将PWM的信号倒相. 一. 工作原理 二.PWM输出 1. 固定脉宽PW ...
- c语言怎么实现模块化vc,原创:在C语言中大概实现VC++中的CArray部分功能的两种方法...
#ifndef __LISTARRAY_H__ #define __LISTARRAY_H__ #include "rtthread.h" #include "finsh ...
最新文章
- NTU 课程笔记:CV6422 置信区间
- OpenCV 距离变换的笔记
- 使用HttpHandler解析并展示PDF文档内容
- 三星三层影像传感器提升拍摄能力 索尼压力倍增
- 核心动画与UIView的区别
- Axios 如何缓存请求数据?
- Fusion组件库是如何支持多语言能力的
- http 标准超时时间_Go 中 http 超时问题的排查
- NHibernate初学者指南(15):使用LINQ to NHibernate提供程序查询数据
- Linux 查看文件修改时间(精确到秒)
- PTA—念数字(C语言)两种方法
- 5月25 python3.6—pymouse—pyhook_3安装问题
- java json 转数据_Java解析(读取)Json数据{}、[{}](转)
- python:删除DataFrame中某列值为NaN的记录/行
- 浙江大学计算机科学与技术博士培养研究方向,浙江大学博士研究生培养方案
- python cv2图片剪裁
- 模拟购物车系统(添加、修改、查询、结算)(Java实现)
- [导入]陈冠希蛰伏35天后闪电复出 将与舒淇演爱情片
- 《梦幻西游H5》L inux搭建游戏服务器!
- 850是什么意思_850w是什么意思 孤存850w是什么梗