扩展枚举功能的两种方法

前言

在上一篇文章中,我解释了如何以及为什么在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而是返回预初始化的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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/343381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【渝粤教育】国家开放大学2018年春季 7403-21T素质与思想政治教育 参考试题

编码&#xff1a;7403 座位号&#xff1a; 2017-2018学年度第二学期期末考试 素质与思想政治教育试题&#xff08;开卷&#xff09; 2018年7月 一、单项选择题&#xff08;每题3分&#xff0c;共30分&#xff09; 考生注意&#xff1a;请将答案填写在下面的方框内&#xff0c;…

工业交换机和工业级光纤收发器的区别

工业交换机和工业级光纤收发器都是网络数据传输设备中的重要组成部分。工业交换机是进行网络内数据交换的一种以太网连接设备&#xff0c;而工业级光纤收发器是延长传输距离的一种光电转换设备。那么他们之间具体有哪些不同之处呢&#xff1f;接下来就由飞畅科技的小编来为大家…

【渝粤教育】国家开放大学2018年春季 8635-21T老年人中医体质辨识与养 参考试题

科目编号&#xff1a;8635 座位号 2017-2018学年度第二学期期末考试 老年人中医体质辨识与养生保健试题 2018年6月 一、名词解释&#xff08;本大题共5小题&#xff0c;每小题4分&#xff0c;共计20分&#xff09; 1.拔罐 2.炙法 3.气虚体质 4.阳虚体质 5.痰湿体质 二、填…

java.lang.stackoverflowerror_java.lang.StackOverflowError——如何解决StackOverflowError错误

StackOverflowError在程序栈空间耗尽时抛出&#xff0c;通常是深度递归导致。StackOverflowError继承了VirtualMachineError类&#xff0c;后者表示JVM已被破坏或资源耗尽。更进一步&#xff0c;VirtualMachineError继承自Error类&#xff0c;应用程序不应该捕获这种严重的错误…

【渝粤教育】国家开放大学2018年春季 8664-21T文书档案管理 参考试题

试卷编号&#xff1a;8664 座位号 2017——2018学年度第二学期期末考试 文 书 档 案 管 理 试 题 2018年7月 1&#xff0e;从文书的形成和使用上划分&#xff0c;文书可分为 和 两类。 2&#xff0e;文头一般由公文名称&#xff08;又称版头&#xff09;、 、 、 、 等部组成…

couchbase_Spring Data Couchbase 1.0 GA发布

couchbaseSpring Data Couchbase 1.0 GA版本已发布&#xff01; 该项目是Spring Data项目的一部分&#xff0c;该项目旨在为新数据存储提供熟悉且一致的基于Spring的编程模型&#xff0c;同时保留特定于存储的功能。 Spring Data Couchbase项目提供了与Couchbase Server数据库…

工业交换机厂家,工业交换机品牌前十大排名

工业交换机&#xff0c;也叫工业以太网交换机。随着物联网的快速发展&#xff0c;工业交换机市场竞争也越来越激烈。国内的工业交换机厂家&#xff0c;像北京东土、深圳三旺、杭州飞畅科技等都有自己的研发团队&#xff0c;产品质量和进口的几乎没有差别&#xff0c;但是价格比…

【渝粤教育】国家开放大学2018年秋季 0043-22T计算机文化 参考试题

编号&#xff1a;0053 座位号&#xff1a; 2018&#xff5e;2019学年度第一学期期末考试 立体构成 试题 2019年1月 一、实作题&#xff08;100分&#xff09; 运用所学知识&#xff0c;完成一件“渐变层面构造”的立体构成作品。 要求&#xff1a;1.所用材料为纸质材料。 2.作…

设计php框架_自制PHP框架之设计模式

为什么要使用设计模式&#xff1f;设计模式&#xff0c;我的理解是为了达到“可复用”这个目标&#xff0c;而设计的一套相互协作的类。感兴趣的读者可以阅读《Design Patterns: Elements of Reusable Object-Oriented Software》&#xff0c;四位作者(Gang of Four)在书中列举…

Java将列表转换为数组,反之亦然

介绍&#xff1a; 在本文中&#xff0c; 我们将快速学习如何将Java List &#xff08;例如ArrayList &#xff09;转换为数组&#xff0c;反之亦然。 如果您希望总体上了解有关ArrayList的更多信息&#xff0c;请随时阅读我们有关Java ArrayLists的文章。 同时&#xff0c;让我…

工业交换机选择时需要注意什么?

工业以太网交换机是用于连接以太网的设备&#xff0c;应用十分广泛&#xff0c;主要应用于&#xff1a;煤矿安全、轨道交通、工厂自动化、水处理系统、城市安防等。现如今&#xff0c;市场上的工业交换机品牌厂家有很多&#xff0c;如何选购合适的以太网交换机是件令人困扰的事…

【渝粤教育】国家开放大学2018年秋季 0266-22T设计构成 参考试题

试卷编号&#xff1a;0272 座位号 2018—2019年度第一学期期末考试 创建小企业 试题 一、名词解释&#xff08;每小题10分&#xff0c;共40分&#xff09; 1.产品 商业模式 创业计划 市场定位 二、单项选择题&#xff08;每小题3分&#xff0c;共30分&#xff0c;每小题只…

html如何与php,html页面怎么跟php文件连接

HTML页面调用PHP文件的方法是要通过JavaScript来实现&#xff0c;在生成静态页面时&#xff0c;可以根据数据库id给html页面生成一个对应的JavaScript文件来调用PHP文件。HTML页面调用PHP文件的方法是要通过JavaScript来实现&#xff0c;在生成静态页面时&#xff0c;可以根据数…

【渝粤教育】国家开放大学2018年秋季 0350-21T幼儿园课程论 参考试题

试题编号&#xff1a;0365 座位号 2018-2019 学年度第一学期期末考试 电子商务概论 试题 一、名词解释题: &#xff08;每题5分&#xff0c;共20分&#xff09; 1.网上问卷调研法: 2&#xff0e;社交网络营销&#xff1a; 银行卡线上支付&#xff1a; 第三方物流企业配送&am…

为使节构建控制平面的指南第3部分-特定于域的配置API

这是探索为Envoy Proxy构建控制平面的系列文章的第3部分。 在本博客系列中&#xff0c;我们将研究以下领域&#xff1a; 采用一种机制来动态更新Envoy的路由&#xff0c;服务发现和其他配置 确定哪些组件构成了控制平面&#xff0c;包括后备存储&#xff0c;服务发现API&…

工业交换机中:千兆级别和快速级别传输效率对比

目前市面上的工业交换机种类繁多&#xff0c;我们在采购工业交换机的时候&#xff0c;一般都是按照传输速率来进行挑选的。千兆工业交换机和快速以太网交换机都是传输速率比较快的工业交换机&#xff0c;那么&#xff0c;他们之间有哪些区别呢&#xff1f;接下来就由飞畅科技的…

《操作系统A》期末考试复习题——大题51-62(手写笔记)

51、如果限制为两道的多道程序系统中&#xff0c;有4个作业进入系统&#xff0c;其进入系统时刻、估计运行时间为下图所示。系统采用SJF作业调度算法&#xff0c;采用SRTF进程调度算法。作业进入系统时刻、估计运行时间如下&#xff1a; 作业 进入系统时刻 估计运行时间/min …

【渝粤教育】国家开放大学2018年秋季 0553-21T色彩 参考试题

编号&#xff1a;0557 座位号&#xff1a; 2018&#xff5e;2019学年度第一学期期末考试 广告设计与制作 试题 2019年1月 一、设计题1&#xff08;每小题30分&#xff0c;共30分&#xff09; 请将“九州四海”设计成毎字44cm左右的与文字意义有直接联系的变形创意广告字体。 要…

php 登录记住密码,php 记住密码自动登录

做网站的时候会碰到记住密码&#xff0c;下次自动登录&#xff0c;一周内免登陆&#xff0c;一个月内免登陆这种需求。这种功能一般通过cookie来实现的。用户在登陆的时候&#xff0c;如果选择了记住密码或者一周内免登陆等这个选项的时候&#xff0c;则在用户成功登陆操作完成…

java8 默认方法_如何不使用Java 8默认方法

java8 默认方法警告&#xff1a;一旦阅读&#xff0c;您将无法看不到它 我在上一篇博客文章中讨论了默认方法的多重继承&#xff0c;以及它们在编译和运行时的行为。 这周&#xff0c;我将研究如何使用默认方法进行真正的继承&#xff0c;实际上&#xff0c;默认方法并非为之设…