自动处理重复的代码

在本文中,我将描述如何使用Java :: Geci生成器Repeated以克服泛型不能为原始类型的Java语言不足。 该示例是对Apache Commons Lang库的建议扩展。

介绍

当您复制粘贴代码时,您做错了什么。 至少那是感知。 您必须创建更通用的代码结构,以便可以多次使用不同的参数而不是类似的代码。

这并非总是如此。 有时您必须重复一些代码,因为您使用的语言(尚)不支持该问题所需的功能。

这太抽象了。 让我们看一个特定的示例,以及如何使用在Java :: Geci框架中运行的Repeated源生成器来管理它。

问题

Apache Commons Lang库中的org.apache.commons.lang3.Functions类定义了一个内部接口FailableFunction 。 这是一个通用接口,定义为

 @FunctionalInterface public interface FailableFunction<I, O, T extends Throwable> { /** * Apply the function. * @param pInput the input for the function * @return the result of the function * @throws T if the function fails */ O apply(I pInput) throws T; } 

这本质上与Function<I,O> ,后者将I转换为O但是由于接口是有故障的,因此它也可能引发类型T的异常。

新的需要是

 public interface Failable<I>Function<O, T extends Throwable> 

每个<I>原语值的面。 问题是泛型在Java中还不能是原始的,因此我们应该为每种原始类型分离接口,例如

 @FunctionalInterface public interface FailableCharFunction<O, T extends Throwable> { O apply( char pInput) throws T; } @FunctionalInterface public interface FailableByteFunction<O, T extends Throwable> { O apply( byte pInput) throws T; } @FunctionalInterface public interface FailableShortFunction<O, T extends Throwable> { O apply( short pInput) throws T; } @FunctionalInterface public interface FailableIntFunction<O, T extends Throwable> { O apply( int pInput) throws T; }  ... and so on ... 

这是许多非常相似的方法,可以很容易地由模板描述,然后由某些代码生成工具生成。

使用Java :: Geci进行模板处理

Java :: Geci框架附带了许多现成的生成器。 其中之一是功能强大的Repeated生成器,正是出于这个目的。 如果必须重复使用可能的参数的代码,则可以定义模板,值和Repeated将生成解析模板参数的代码。

向POM添加依赖项

我们要做的第一件事是将Java :: Geci依赖项添加到pom.xml文件中。 由于Apache Commons Language仍基于Java 8,因此我们必须使用Java :: Geci 1.2.0的Java 8反向端口:

 < dependency > < groupId >com.javax1.geci</ groupId > < artifactId >javageci-core</ artifactId > < version >1.2.0</ version > < scope >test</ scope > </ dependency > 

注意,依赖项的范围是testRepeated生成器可以方便地使用,而字节码中不会保留任何Geci注释,因此它们是编译时的依赖项。 实际上,所有生成器都可以使用而无需注释,因此不需要任何编译依赖关系,而这对于生产来说是额外的依赖关系。 在Repeated的情况下,这甚至很容易做到。

运行发电机的单元测试

我们要做的第二件事是创建一个将执行生成器的单元测试。 Java :: Geci生成器在单元测试阶段运行,因此它们可以使用反射以及实际的源代码访问已编译的代码。 如果生成的任何代码与源文件中已有的代码不同,则测试将失败,并且应再次执行生成过程。 由于发生器是(应该)幂等的,因此测试第二次不应失败。

以我的经验,很遗憾,此工作流程会影响开发人员的行为。 运行测试/失败,再次运行! 这是一个糟糕的循环。 有时我碰巧发现自己不是在代码生成器发生故障的情况下重新执行单元测试。 但是,这就是Java :: Geci的工作方式。

有一些关于Java :: Geci工作流程的文章

  • 您的代码是多余的,请继续使用!
  • 保持JavaDoc为最新
  • 将对象转换为映射并返回
  • 反射选择器表达式
  • 使用Java :: Geci生成Getter和Setters
  • 创建一个Java :: Geci生成器
  • 如何生成源代码

因此,在此我不会重复总体架构及其工作流程。

单元测试如下:

 @Test void generatePrimitiveFailables() throws Exception { final Geci geci = new Geci(); Assertions.assertFalse(geci.source(Source.maven().mainSource()) .only( "Functions" ) .register(Repeated.builder() .values( "char,byte,short,int,long,float,double,boolean" ) .selector( "repeated" ) .define((ctx, s) -> ctx.segment().param( "Value" , CaseTools.ucase(s))) .build()) .generate(), geci.failed()); } 

调用source()register()only()配置框架。 此配置告诉框架使用项目主Java src目录中的源文件,并仅使用文件名"Functions" 。 在我们调用generate()开始代码生成之前,对register()的调用会register() Repeated生成器实例。

生成器实例本身是使用内置的生成器创建的,该生成器使我们可以配置生成器。 在这种情况下,对values()的调用定义了我们要重复使用模板的逗号分隔的值列表(稍后在注释中的代码中定义)。 对selector()的调用定义了此代码重复代码的标识符。 单个源文件可能包含多个模板。 每个模板可以使用不同的值列表进行处理,结果将插入到不同的输出段中,并插入源文件中。 在这种情况下,仍然只有一个这样的代码生成模板,它必须用一个名称标识,并且该名称也必须在editor-fold部分中使用,该部分是生成代码的占位符。

实际使用生成器的名称有两个作用。 其一是它标识了编辑器折叠段和模板。 另一个是框架将看到带有该标识符的编辑器折叠段,并且它将识别出该源文件需要该生成器的注意。 另一种可能性是在类中添加@Repeated@Geci("repeated")批注。

如果标识符是别的东西并且不repeated那么生成器“ Repeated将不会触及源代码,或者我们需要将另一个段标识为“ repeated ,除了触发生成器之外,它实际上不会被使用。

define()的调用define()了一个BiConsumer ,该BiConsumer获取了上下文引用和实际值。 在这种情况下, BiConsumer计算大写值,并将其放入与名称Value相关联的实际细分参数集中。 默认情况下,实际值与名称value关联,并且传递给方法define()BiConsumer可以定义和注册其他参数。 在这种情况下,它将添加新值

 value      Value  char   --> Char  byte   --> Byte  short  --> Short  int    --> Int  long   --> Long  float  --> Float  double --> Double  boolean --> Boolean 

源代码

第三件事是在源文件中准备模板和输出段。

输出段的准备非常简单。 它只是一个编辑器折叠:

 //<editor-fold id="repeated"> //</editor-fold> 

生成的代码将自动插入两行之间,并且编辑器(Eclipse,IntelliJ或NetBeans)将允许您关闭折叠。 您不想编辑此代码:它已生成。

该模板将如下所示:

 /* TEMPLATE repeated @FunctionalInterface public interface Failable{{Value}}Function<O, T extends Throwable> { O apply({{value}} pInput) throws T; } */ 

代码生成器找到模板的开始,以查找与/* TEMPLATE name格式匹配的行,并收集连续的行,直到注释的结尾。

该模板使用小胡子模板的占位符格式,即用双括号括起来的值的名称。 双括号在Java中很少见。

当我们运行单元测试时,它将生成我在本文开头已经列出的代码。 (然后,它当然会失败:修改了源代码,然后再次编译。)

摘要和总结

最重要的要点和警告:源代码生成是旨在弥补编程语言不足的一种工具。 不要使用代码生成来修正语言的不足,而不仅仅是语言的不足,技能或知识的不足。 生成代码的简单方法不是生成不必要的冗余代码的借口。

另一个收获是,在Java中使用此生成器非常容易。 该功能可以与Java所没有的C预处理器相媲美,而且效果良好。 需要时使用它。 即使依赖项的设置和单元测试的开销可能很小,但可维护性通常会偿还此成本。

翻译自: https://www.javacodegeeks.com/2019/09/handling-repeated-code-automatically.html

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

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

相关文章

第三章 搜索与图论(二)

文章目录最短路朴素Dijkstra算法堆优化版的Dijkstra算法Bellman-Ford算法SPFA算法求距离判负环Floyd最短路 并不区分有向图和无向图&#xff0c;因为无向图是一种特殊的有向图。直接用有向图的算法&#xff0c;解决无向图的问题。 常见情况可以分为两大类 在图论中&#xff0…

第三章 搜索与图论(三)

文章目录朴素版PrimKruskal算法染色法匈牙利算法朴素版Prim 给定一个 n 个点 m 条边的无向图&#xff0c;图中可能存在重边和自环&#xff0c;边权可能为负数。求最小生成树的树边权重之和&#xff0c;如果最小生成树不存在则输出 impossible。给定一张边带权的无向图 G(V,E)&a…

JDK 14:记录,文本块等

今天的Mark Reinhold帖子“ 建议JEP针对JDK 14&#xff1a;305、343、359、366和368 ”提议针对JDK 14再针对五个JEP。 该组中有一些备受期待的功能&#xff0c;尽管建议除“预览”或“孵化”之外的所有功能&#xff1a; JEP 305 &#xff1a;instanceof的模式匹配&#xff08…

CDF 图的含义

CDF 图用于创建经验累积分布函数图。 使用 CDF 图可以确定等于或小于 x 轴上的给定值的数据的百分比。 例如&#xff0c;在该 CDF 图中&#xff0c;大约 34% 的数据小于总脂肪值 10 克。 参考链接 1. https://www.jmp.com/support/help/zh/14-2/ba-distribution-22.shtml

Typora表格中常用操作

快捷键行为Enter跳出表格Shift Enter表格内换行Alt Enter添加一行Alt 方向键向上/下移动该行

rome rss_RSS阅读器使用:ROME,Spring MVC,嵌入式Jetty

rome rss在这篇文章中&#xff0c;我将展示一些创建Spring Web应用程序的指南&#xff0c;这些应用程序使用Jetty并使用名为ROME的外部库运行RSS来运行它。 一般 我最近创建了一个示例Web应用程序&#xff0c;充当RSS阅读器。 我想检查ROME以阅读RSS。 我还想使用Spring容器和…

Ubuntu系统输入中文方式

我目前知道Ubuntu有两个还算好用的中文输入法&#xff1a; Fcitx&#xff1a;它是Linux世界开源的输入法框架&#xff0c;提供 Google PinYin、ShuangPin、SunPinYin、Wubi、ZhengMa、Hong Kong 和 TaiWan繁体等输入法。 1 安装Fcitx sudo apt install fcitx-pinyin fcit…

针对JDK 14提议的另外六个JEP

Mark Reinhold最近的消息宣布了六个新的“建议针对JDK 14的JEP ”&#xff1a; JEP 345 &#xff0c; JEP 361 &#xff0c; JEP 363 &#xff0c; JEP 364 &#xff0c; JEP 365和JEP 367 。 假设没有异议由11月7日提出&#xff0c;这些JEPs将有针对性地JDK 14与之前定位JEPs沿…

VMWare建立于W10的共享文件夹

一、共享文件夹建立 在虚拟机设置 -> 文件夹共享&#xff0c;选择总是启用&#xff0c;点击添加&#xff1a; 直接点击下一步&#xff1a; 选择原系统共享文件夹位置&#xff0c;并命名&#xff1a; 选择启用此共享&#xff0c;并继续&#xff1a; 二、VMtools安装 虚拟机…

Ubuntu中root用户和user用户的相互切换

Ubuntu是最近很流行的一款Linux系统&#xff0c;因为Ubuntu默认是不启动root用户&#xff0c;现在介绍如何进入root的方法。 &#xff08;1&#xff09;从user用户切换到root用户 不管是用图形模式登录Ubuntu&#xff0c;还是命令行模式登录&#xff0c;我们会发现缺省的用户…

VSCode如何去掉Monokai主题下的绿色下划线

VScode中类似sublime的主题为Monokai&#xff0c;但是自带主题Monokai中绿色下划线令人不舒服。 在网上寻找多种方式去除下划线。终于找到一种合适的处理方式。 1 安装主题插件 在主题插件中搜索One Monokai Theme&#xff0c;下载并安装 2 配置全局主题 通过快捷键“Ctr…

nio 读取目录所有文件_在NIO.2中使用文件和目录

nio 读取目录所有文件在先前的文章中&#xff0c;我讨论了文件和目录的创建&#xff08; 创建文件和目录 &#xff09;以及选择&#xff08; 列出和过滤目录内容 &#xff09;。 采取的最后一个合乎逻辑的步骤是探索我们如何使用它们以及如何使用它们。 这是库的一部分&#xf…

week1

文章目录找出数组中重复的数字不修改数组找出重复的数字二维数组中的查找替换空格从尾到头打印链表重建二叉树二叉树的下一个节点用两个栈实现队列斐波那契数列旋转数组的最小数字矩阵中的路径找出数组中重复的数字 https://www.acwing.com/problem/content/description/14/ …

使用反射前先三思

介绍 有时&#xff0c;作为开发人员&#xff0c;您可能会遇到无法使用new运算符实例化对象的情况&#xff0c;因为其类名存储在配置XML中的某个位置&#xff0c;或者您需要调用一个名称指定为注释属性的方法。 在这种情况下&#xff0c;您总会有一个答案&#xff1a;“使用反射…

解决 ZLibrary 登录/注册不了的问题

一 文章转载链接内容 转载链接&#xff1a;解决 ZLibrary 登录/注册不了的问题 - 知乎 很多小伙伴反馈说 Z-Library 能打开&#xff0c;但是不能登录。这实际上是由于官方登录入口受限导致的。话虽如此&#xff0c;我们仍然可以通过某些方法绕过这个限制。 >虽然我们注册时…

阿里云资源搜索网站

1. 奈斯搜索 - 资源超丰富的阿里云盘资源搜索网站&#xff01; 2. 求资源-百度网盘资源分享_云盘资源分享网_云盘资源网 3. 猫狸盘搜 - 阿里云盘搜索神器 4. 云盘资源分享_云盘网盘资源共享_云盘资源搜索_阿里云盘资源-盘友社区

gradle入门_Gradle入门:简介

gradle入门Gradle是一种构建工具&#xff0c;可以用基于Groovy编程语言的内部DSL替换基于XML的构建脚本。 最近它吸引了很多关注&#xff0c;这就是为什么我决定仔细研究一下。 这篇博客文章是我的Gradle教程的第一部分&#xff0c;它有两个目标&#xff1a; 帮助我们安装Gr…

解决Chrome窗口总保持在最前面的问题 get rid of Chrome window always-on-top behavior

今天发现Chrome的窗口总保持在最前面&#xff0c;本以为是自己不小心把“总在最前”的功能打开了呢。结果找了半天也没发现Chrome有这个设置&#xff0c;于是想到了可能是个bug。我上网搜了下&#xff0c;发现大家遇到了跟我一样的问题&#xff0c;而且有人也向google反馈了这个…

Spring MVC – HTTP消息转换器

通常&#xff0c;您需要为用户提供相同的数据&#xff0c;但格式不同&#xff0c;例如JSON&#xff0c;PDF&#xff0c;XLS等。如果您的应用程序是基于Spring Framework的&#xff0c;则可以使用HTTP消息转换器来完成此任务。 需要将HTTP请求&#xff08;或其部分&#xff09;…

C++ 链表的建立与使用

#include <iostream> using namespace std; struct ListNode {int val; //存储数据ListNode *next; //next指针ListNode(int x) : val(x), next(NULL) {} }; ListNode* creatLinkedList(int arr[],int n) {if(n 0)return NULL;ListNode* head new ListNode(arr[…