扩展方法 枚举值_扩展枚举功能的两种方法

扩展方法 枚举值

前言

在上一篇文章中,我解释了如何以及为什么在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

扩展方法 枚举值

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

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

相关文章

正则表达式中关于字符集的问题

/[abc]/&#xff0c;匹配1或者多个方括号中的任意字符&#xff0c;方括号中的字符是“或者”的关系&#xff0c;等价于 /(a|b|c)/ /(abc)/&#xff0c;匹配1个或者多个 abc&#xff0c;abc 是一个整体&#xff0c;如下图所示&#xff1a; /[0-9|_|-]/&#xff0c;匹配由数字…

spring 构造函数注入_Spring依赖注入–字段vs设置器vs构造函数注入

spring 构造函数注入欢迎使用Spring Dependency Injection –字段&#xff0c;设置器&#xff0c;构造函数注入教程。 了解场注入 &#xff0c; 二传手注入和构造函数注入之间的区别。 借助代码示例&#xff0c;我们将看到使用每个示例的好处以及为什么选择一个而不是另一个。 …

java 快速io_Java编程在ICPC快速IO实现源码

本文将介绍Java在ICPC快速IO实现方法&#xff0c;下面看看具体代码。不处理EOF&#xff1a;import java.io.OutputStream;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import java.util.Arrays;import java.util.Random;import java.io…

adf开发_如何在ADF中将参数传递给ActionListener

adf开发在某些情况下&#xff0c;需要将值传递给ADF Button的ActionListener。 可以由actionListeners调用的方法只有一个ActionEvent类型的参数。 因此&#xff0c;我将解释如何将参数传递给该bean方法&#xff0c;但是它在方法签名中仅包含一个参数ActionEvent。 我在页面…

JavaScript/JS的学习

文章目录JavaScript 简介JavaScript 发展史ECMAScript基本语法与 HTML 结合方式数据类型类型转换非 number 转成 number非 boolean 转成 boolean特殊语法变量运算符双等号&#xff08;&#xff09;全等号&#xff08;&#xff09;流程控制语句switch...casewhilefor 循环对象Fu…

java soap协议头_自己调用webservice方法总结(带请求头SoapHeader)

调用webservice总结&#xff1a;1.加入第三方的jar包 Ksoap2-android-XXX2.访问响应的webservice的网站&#xff0c;查看响应的信息&#xff0c;得到nameSpace&#xff0c;methodName&#xff0c;url&#xff0c;soapAction3.如果request信息还有带有SoapHander的。那么就要封装…

注册界面演示代码(前端开发)

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>注册</title><style>* {margin: 0px;padding: 0px;/*这样的设置会阻止元素大小的改变&#xff0c;因为设置内边距时元素大小会发生改变*/b…

apache ignite_使用Apache Ignite瘦客户端– Apache Ignite内部博客

apache ignite从2.4.0版本开始&#xff0c;Apache Ignite引入了一种连接到Ignite群集的新方法&#xff0c;该方法允许与Ignite群集进行通信而无需启动Ignite客户端节点。 从历史上看&#xff0c;Apache Ignite提供了客户端和服务器节点两个概念。 点燃旨在用作轻量级模式的客户…

java 网络编程 方式_JAVA网络编程

概念BIO 阻塞io&#xff0c;1.4之前NIO no-blocking io 非阻塞io&#xff0c;jdk1.4AIO 异步io&#xff0c;jdk1.7浏览器输入网址&#xff0c;敲下回车之后发生了什么&#xff1f;1.URL解析2.DNS解析概念&#xff1a;Domain Name System&#xff0c;域名系统&#xff0c;本质…

如何设置背景图(前端开发)

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>模糊化背景图</title><style>body {background-image: url("image/bjt03.jpg");background-repeat: no-repeat;background-atta…

java字符串底层实现_「JAVA」细述合理创建字符串,分析字符串的底层存储,你不该错过...

Java基础之字符串操作——String字符串什么是字符串&#xff1f;如果直接按照字面意思来理解就是多个字符连接起来组合成的字符序列。为了更好的理解以上的理论&#xff0c;我们先来解释下字符序列&#xff0c;字符序列&#xff1a;把多个字符按照一定的顺序排列起来&#xff1…

查看oracle会话和进程_带有Oracle Digital Assistant和Fn Project的会话式UI。 第三部分,迁移到云...

查看oracle会话和进程在这篇文章中&#xff0c;我将继续在Oracle Digital Assistant和Oracle Digital Assistant之上为FlexDeploy实现对话式UI的故事。 Fn项目 。 今天&#xff0c;我将围绕聊天机器人工作的无服务器API迁移到云中&#xff0c;因此整个解决方案都在云中工作&am…

浏览器用户脚本管理器(Tampermonkey)

文章目录脚本管理器的好处是什么&#xff1f;用户脚本管理器有哪些&#xff1f;如何使用&#xff1f;安装脚本示例常用脚本脚本管理器的好处是什么&#xff1f; 用户脚本管理器将在您的用户脚本管理方面提供更多的便利。 它提供了诸如便捷脚本安装、自动更新检查、标签中的脚本…

服务器$java_top路径_ERP实施项目-MD120_FIN000_即时打印_V1.0

中国移动项目即时打印开发在此将web的即时打印开发成通用程序。1、首先通过创建rtf模板制定报表的输出格式&#xff1b;2、将需要输出的数据插入客户化表cux_fnd_xml_pool中&#xff1b;3、通过EBS功能调用CuxFndXmlpViewer.jsp。此jsp程序会根据传入的参数判断调用哪个rtf模板…

通讯网关 api网关_API网关正在经历身份危机

通讯网关 api网关这些年来&#xff0c;API网关正在经历一些身份危机 。 它们是否是集中的共享资源&#xff0c;从而促进了API对外部实体的公开和治理&#xff1f; 它们是集群入口哨兵&#xff0c;可以严格控制哪些用户流量进入或离开集群吗&#xff1f; 还是他们根据自己拥有…

百度搜索引擎使用技巧

1.在指定的网站搜索内容 在搜索框输入&#xff1a;site:youku.com 蛋炒饭&#xff0c;则只会返回优酷网站上关于蛋炒饭的内容 2.屏蔽指定的网站 例如&#xff0c;搜索“内存溢出”&#xff0c;但是不希望看到 csdn 网站相关的内容&#xff0c;你可以在搜索框输入&#xff1a;内…

docker 容器监控_以简便的方式监控Docker容器中的ADF应用程序

docker 容器监控在这篇简短的文章中&#xff0c;我将展示一种简单的方法来确保在Docker容器中运行的ADF应用程序在内存利用率方面是健康的Java应用程序。 我将使用标准工具JConsole&#xff0c;它是计算机上JDK安装的一部分。 如果存在问题&#xff08;例如&#xff0c;内存泄漏…

phpexcel 日期 时分秒_thinkPHP+PHPExcel实现读取文件日期的方法含时分秒

Vendor(PHPExcel.PHPExcel.IOFactory);$inputFileName Public/demo/demo.xls;$objReader new PHPExcel_Reader_Excel5();$objPHPExcel $objReader->load($inputFileName);$sheet $objPHPExcel->getSheet(0);$highestRow $sheet->getHighestRow(); // 取得总行数$…

css中的display属性值:table,table-row,table-cell

table&#xff1a;此元素会作为块级表格来显示&#xff08;类似 <table>&#xff09;&#xff0c;表格前后带有换行符。 table-row&#xff1a;此元素会作为一个表格行显示&#xff08;类似 <tr>&#xff09;。 table-cell&#xff1a;此元素会作为一个表格单元格显…