java 7.函数-递归

您一直在听到将要席卷全球的函数式编程,而您仍然坚持使用普通Java? 不用担心,因为您已经可以在日常Java中添加一些功能样式。 此外,它很有趣,可以节省许多代码行并减少错误。

什么是谓词?

实际上,当我很早以前在Java 1.4中进行编码时,我第一次发现Apache Commons Collections时就爱上了谓词。 该API中的谓词不过是Java界面,仅包含一种方法:

evaluate(Object object): boolean

就是这样,它只需要一些对象并返回true或false。 带有Apache License 2.0的Google Guava是Apache Commons Collections的更新版本。 它使用通用参数通过一种方法定义了谓词接口:

apply(T input): boolean

就这么简单。 要在应用程序中使用谓词,您只需在自己的单个方法apply(something)中使用您自己的逻辑来实现此接口。  

一个简单的例子

作为早期的示例,假设您有一个PurchaseOrder对象的列表订单 ,每个订单都有一个日期,一个Customer和一个州。 各种用例可能会要求您找出该客户的每个订单,每个待处理,已发货或已交付的订单,或者自上一小时以来完成的每个订单。 当然,您可以通过foreach循环和if内部循环来实现:

//List<PurchaseOrder> orders...public List<PurchaseOrder> listOrdersByCustomer(Customer customer) {final List<PurchaseOrder> selection = new ArrayList<PurchaseOrder>();for (PurchaseOrder order : orders) {if (order.getCustomer().equals(customer)) {selection.add(order);}}return selection;
}

再次针对每种情况:

public List<PurchaseOrder> listRecentOrders(Date fromDate) {final List<PurchaseOrder> selection = new ArrayList<PurchaseOrder>();for (PurchaseOrder order : orders) {if (order.getDate().after(fromDate)) {selection.add(order);}}return selection;
}

重复非常明显:除了if子句中的条件(此处以黑体强调)之外,每个方法都是相同的。 使用谓词的想法只是通过调用谓词来代替if子句中的硬编码条件,然后调用谓词。 这意味着您只能编写一个方法,将谓词作为参数,并且仍然可以覆盖所有用例,甚至已经支持了您尚不知道的用例:

public List<PurchaseOrder> listOrders(Predicate<PurchaseOrder> condition ) {final List<PurchaseOrder> selection = new ArrayList<PurchaseOrder>();for (PurchaseOrder order : orders) {if (condition.apply(order)) {selection.add(order);}}return selection;
}

每个谓词可以在多个地方使用时定义为独立类,也可以定义为匿名类:

final Customer customer = new Customer("BruceWaineCorp");
final Predicate<PurchaseOrder> condition = new Predicate<PurchaseOrder>() {public boolean apply(PurchaseOrder order) {return order.getCustomer().equals(customer);}
};

使用真正的函数式编程语言(Scala,Clojure,Haskell等)的朋友会评论说,上面的代码非常冗长,无法完成某些非常常见的事情,我必须同意。 但是,我们已经习惯了Java语法中的冗长性,并且我们拥有功能强大的工具(自动完成,重构)来适应它。 而且我们的项目可能无法在一夜之间切换到另一种语法。  

谓词是收藏最好的朋友

回到我们的示例,我们只编写了一次foreach循环来覆盖每个用例,我们对此表示满意。 但是,您的朋友“真正地”进行函数式编程仍然可以嘲笑您必须自己编写的循环。 幸运的是,来自Apache或Google的API也都提供了您可能期望的所有优点,特别是类似于java.util.Collections的类,因此命名为Collections2 (不是一个非常原始的名称)。

此类提供了一个方法filter() ,它的功能类似于我们之前编写的内容,因此我们现在可以完全不使用循环来重写方法:

public Collection<PurchaseOrder> selectOrders(Predicate<PurchaseOrder> condition) {return Collections2.filter(orders, condition);
}

实际上,此方法返回一个筛选视图:

返回的集合是unfiltered的实时视图(输入集合); 改变一个会影响另一个。

这也意味着更少的内存使用,因为是从未经过滤的 过滤 ,以实际返回的集合初始收集没有实际的副本。

在类似的方法上,给定一个迭代器,您可以在它之上请求一个经过过滤的迭代器(Decorator模式),该迭代器仅为您提供谓词选择的元素:

Iterator filteredIterator = Iterators.filter(unfilteredIterator, condition);

由于Java 5的Iterable接口在foreach循环中非常方便使用,因此我们更愿意使用以下表达式:

public Iterable<PurchaseOrder> selectOrders(Predicate<PurchaseOrder> condition) {return Iterables.filter(orders, condition);
}// you can directly use it in a foreach loop, and it reads well:
for (PurchaseOrder order : orders.selectOrders(condition)) {//...
}

现成的谓词

要使用谓词,您可以简单地定义自己的接口谓词,或者为应用程序中需要的每个类型参数定义一个。 这是可能的,但是使用来自诸如Guava或Commons Collections之类的API的标准谓词接口的好处是,API带来了许多出色的构建块,可与您自己的谓词实现结合使用。

首先,您甚至根本不需要实现自己的谓词。 如果您所需要的只是一个对象是否等于另一个条件或不为空的条件,那么您可以简单地要求谓词:

// gives you a predicate that checks if an integer is zero
Predicate<Integer> isZero = Predicates.equalTo(0);
// gives a predicate that checks for non null objects
Predicate<String> isNotNull = Predicates.notNull();
// gives a predicate that checks for objects that are instanceof the given Class
Predicate<Object> isString = Predicates.instanceOf(String.class);

给定一个谓词,您可以将其求逆(true变为false,反之亦然):

Predicates.not(predicate);

使用布尔运算符AND或OR组合多个谓词:

Predicates.and(predicate1, predicate2);
Predicates.or(predicate1, predicate2);
// gives you a predicate that checks for either zero or null
Predicate<Integer> isNullOrZero = Predicates.or(isZero, Predicates.isNull());

当然,您还具有总是返回true或false的特殊谓词,它们确实非常有用,我们将在以后的测试中看到:

Predicates.alwaysTrue();
Predicates.alwaysFalse();

谓词在哪里

我经常经常一开始会做匿名谓词,但是它们总是经常被使用,因此经常被提升为实际的类,无论是否嵌套。

顺便说一下,这些谓词在哪里定位? 遵循罗伯特·马丁( Robert C. Martin) 及其共同封闭原则(CCP) :

一起变化的类,一起属于

因为谓词操纵某种类型的对象,所以我喜欢将它们共置为靠近它们作为参数的类型。 例如, CustomerOrderPredicatePendingOrderPredicateRecentOrderPredicate类应与它们评估的PurchaseOrder类驻留在同一包中,或者如果它们很多,则位于子包中。 另一个选择是将它们嵌套在类型本身内。 显然,谓词与它们所操作的对象非常相关。

资源资源

以下是本文示例的源文件: cyriux_predicates_part1 (zip)

在下一部分中 ,我们将了解谓词如何简化测试,它们如何与域驱动设计中的规范相关联,以及一些其他方面的知识,以使您的谓词发挥最大作用。

参考: 带有谓词的纯Java语言中的函数式风格– Cyrille Martraire博客博客中JCG合作伙伴 Cyrille Martraire的第1部分 。

翻译自: https://www.javacodegeeks.com/2012/05/functional-style-in-java-with.html

java 7.函数-递归

java 7.函数-递归_带有谓词的Java中的函数样式-第1部分相关推荐

  1. java 7.函数-递归_带有谓词的Java中的函数样式-第2部分

    java 7.函数-递归 在本文的第一部分中,我们介绍了谓词,这些谓词通过具有返回true或false的单一方法的简单接口,为Java等面向对象的语言带来了函数式编程的某些好处. 在第二部分和最后一部 ...

  2. python中可以使用变量来引用函数吗_如何在python语言中使用函数变量并调用函数...

    在python语言中,除了常规变量之外,还有函数变量.把函数本身赋值给变量,这个变量为函数变量. 工具/原料 python pycharm 截图工具 WPS 方法/步骤 1 在已新建的python文件 ...

  3. 带有谓词的Java中的函数样式-第2部分

    在本文的第一部分中,我们介绍了谓词,这些谓词通过具有返回true或false的单个方法的简单接口,为Java等面向对象的语言带来了函数式编程的某些好处. 在第二部分和最后一部分中,我们将介绍一些更高级 ...

  4. 带有谓词的Java中的功能样式-第1部分

    您一直在听到将要席卷全球的函数式编程,而您仍然坚持使用普通Java? 不用担心,因为您已经可以在日常Java中添加一些功能样式. 此外,它很有趣,可以节省许多代码行并减少错误. 什么是谓词? 实际上, ...

  5. java 柯里化_函数式编程(Java描述)——Java中的函数及其柯里化

    本文继续上一篇的内容 在Java中,函数可以表现为一个普通的方法.一个lambda表达式,又或者方法引用,甚至是匿名类.本文不会介绍匿名类这种形式. 方法 Java中的方法,Java使用方法这一概念来 ...

  6. java list e 查找_源码(04) -- java.util.ListE

    java.util.List 源码分析(JDK1.7) ------------------------------------------------------------------------ ...

  7. java数组实现队列_使用数组在Java中进行队列实现

    java数组实现队列 什么是队列? (What is a Queue?) Queue is a special type of data structure, which is designed to ...

  8. java新手的通病_编程随想:Java新手的通病 PDF 下载

    编程随想:Java新手的通病 PDF 下载 下载地址: 提取码:onyi 相关截图: 主要内容: 其实很早以前就想写这样一个文章,可惜当时我没有 Blog,所以到现在才写下来.最近几年,随着 Java ...

  9. java主要内存区域_可能是把Java内存区域讲的最清楚的一篇文章

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 介绍下 Java 内存区域(运行时数据区) Java 对象的创建过程(五步,建议能默写出来并且要知道每一步虚拟机做了什么) 对象的访问定位的两种方式(句柄 ...

最新文章

  1. LeetCode简单题之到目标元素的最小距离
  2. python全栈开发笔记---------函数
  3. (节点分类)四大图数据集AIFB,MUTAG,BGS,AM数据集获取
  4. 【译文】 C#面向对象的基本概念 (Basic C# OOP Concept) 第一部分(类,对象,变量,方法,访问修饰符)...
  5. app vue 真机运行_uni-app黑魔法:小程序自定义组件运行到H5平台
  6. 使用Apache Cassandra设置SpringData项目
  7. python3.6.8安装失败_centos7编译安装Python 3.6.8 后用pip3出现SSL未配置问题(import ssl失败)解决方法...
  8. 新iPhone销量将持续走低 因为旧iPhone够用好几年
  9. 探讨一下常见支付系统的对外接口
  10. js如何将跨域打开的窗口放到最前面_程序员的强迫症-便捷打开常用网站
  11. 在一个html中使用另一个html数据,如何为某些HTML标签存储任意数据
  12. 如何保证战略落地_博雅视野丨大健康战略时代,全龄康养如何落地?
  13. spring扩展点二:自定义beanPostProcessor原理解析
  14. 数据库原理及应用教程第四版课后答案
  15. DSP入门前的背景知识
  16. 知物由学 | 用案例起底黑灰产的各种“骚”操作
  17. ROMS海洋模式笔记
  18. 路由器密码重置(更改寄存器的值)
  19. 封装系统之新手操作版
  20. c语言中正弦函数的定义,三角函数基本概念 | 玄数

热门文章

  1. java之String
  2. publiccms实现多层级选项卡效果
  3. 2017蓝桥杯省赛---java---A---2(9数算式)
  4. 利用赫夫曼编码进行数据解压
  5. ListView条目中有CheckBox点击事件失效问题
  6. Mysql字符串截取 mysql将字符串字段转为数字排序或比大小
  7. Windows.etc\hosts文件
  8. OkHttp上传Json嵌套对象
  9. mysql切换用户sql语句,MySQL用户管理及SQL语句详解
  10. springmvc新建拦截器