当然这篇文章并不时髦,但是我希望记录一些干货,主要涉及常用的 函数式接口、Stream,Collector接口及其辅助类、Lambda、异常处理。
本文相关的示例代码
1、函数式接口
然后官方文档比较重要的地方我截取了一下
JDK提供了常见的函数式接口,这里只列出最原始的接口,还有一些接口和这些类似或者是基于这些的。
Consumer
Function
Predicate
Supplier
BiFunction
BinaryOperator
基于这些接口和这些接口的扩展,可以实现一种跨越,典型的例子是jdk8之前,我们写的程序都是命令式的,例如我们要实现加减乘除的运算操作,可以定义四个方法,或者定义一个方法,然后传递接口,那就是所谓的策略模式。但是这样做太复杂。
public int add(int a, int b) {
return a + b;
}
。。。定义四个方法 。。。或者
public int compute(Computor co, int a, int b) { // 策略接口
return co.compute(a, b);
}
这种方式如果使用内部类,会有异常抛不出去的问题,如果单独定义类,就会产生很多Java文件。然而Lambda表达式可以很好的解决。总之一句话,函数式编程传递的参数还是那个pass by value,但是他有更深的实际意义,它传递的是一种行为。我们可以理解为处处传递策略。
2. Stream Collector
Stream的常用方法:filter(Predicate
map(Function
flatMap(Function
另外像reduce(归纳)、distinct(去重)、sorted(排序,默认自然顺序)、peek(偷看,实际上就是对流中当前的元素进行Consumer,不会对其造成影响)、limit(限制流的执行次数)、skip(从当前流中的位置跳过几个)、forEach、collect、count、parallel(并行流,基于fork-join)、sequential(串行流)
collect方法单独说一下,这是核心中的核心,无敌的操作。
本质上就是Collector接口在背后进行一系列的骚操作:
我推荐大家去把Collector接口的所有注释都读一遍,这里只截取重点:
Collector
Supplier supplier();
supplier方法就是用户自定义的中间结果提供者
BiConsumer accumulator();
把T累加到A中
BinaryOperator combiner();
把上次的A合并到新的A中,并且返回新的A
Function finisher();
把A转换成R,返回R
Set<>Characteristics characteristics(); // 下面具体解释
Collector特征:
enum Characteristics {
CONCURRENT,
UNORDERED,
IDENTITY_FINISH
}
更详细请查看demo中的自定义Collector接口的代码
如果理解好此接口,那么辅助类Collectors的那些操作都很好理解了,我们可以任意的定义特别复杂的收集器。
建议大家阅读整个Collectors的源码,理解好groupingBy方法的源码。
3.Lambda
前面已经说过了,函数式接口的实例可以被Lambda表达式、方法引用、构造引用创建
Lambda普通用法相信大家都很熟悉了,这里主要说明Lambda的特殊情况:
方法引用和构造引用分为4类:
1. class_name::static_mathod_name
类中声明的静态方法可以这样引用
2. reference_name(object reference)::instance_method_name
实力方法引用
3. class_name::instance_method_name
// 第一个参数是调用此函数的引用
// 关键在于 是谁去调用注意顺序
建议查看GitHub上的例子以便理解
4. class_name::new
新生成一个对象
关于异常处理
我们自定义函数式接口呗,无非在JDK8的接口的基础上加上 throw Exception 不就好了么,但是事情往往不那么容易。首先,JDK8的Stream,Optional等等都是要基于JDK8本身的接口,我们自己写了一套那么我们自己用的爽,他本身的库函数就不爽了,再加上网络上其他的框架,不就没法兼容了么。
所以我们只能封装聚合一层,把JDK8的函数式接口来一个“倒卖二手货操作”:
实例代码中已经把43个函数式接口全部提供了一份unchecked的版本了哦,欢迎大家来star!