JDK8新特性总结日记

当然这篇文章并不时髦,但是我希望记录一些干货,主要涉及常用的 函数式接口、Stream,Collector接口及其辅助类、Lambda、异常处理。

本文相关的示例代码

1、函数式接口

定义很简单,注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在

然后官方文档比较重要的地方我截取了一下

1.函数式接口有且只有一个抽象方法,默认方法不是抽象方法,不影响函数式接口的要求;2.如果接口声明的抽象方法和Object类中的方法一样,不会导致函数式接口的抽象方法数量加一
1.函数式接口的实例可以被Lambda表达式、方法引用、构造引用创建;2.这个注解只能修饰接口,并且满足函数式接口的要求,否则jvm编译报错
也就是说, 如果某个接口只有一个抽象方法但是并没有用注解去修饰那么编译器依旧会将该接口看作是函数式接口

JDK提供了常见的函数式接口,这里只列出最原始的接口,还有一些接口和这些类似或者是基于这些的。

Consumer<T>:此接口接收一个参数不返回值。顾名思义,传进来的T会被消费,不会返回任何东西。另外接口中有一个andThen方法,这是一个默认方法,他会执行当前对象的accept方法然后执行参数的accept方法。

Function<T, R>:消费T,然后返回R,比如Stream的map操作,rxjava的map操作都是这个原理。同样接口中有两个默认方法,andThen,先执行当前方法的apply,返回的结果作为参数的输入,compose方法相反。具体细节建议看源码。

Predicate<T>:消费T,返回布尔值,另外其接口中的默认方法比较简单,建议读一下源码。

Supplier<T>:不接收参数,返回T

BiFunction<T, U, R>:Function的升级版本,接收两个参数返回一个结果

BinaryOperator<T>:BiFunction的特例,接收两个参数全是T,返回也是T


基于这些接口和这些接口的扩展,可以实现一种跨越,典型的例子是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<T> predicate)  上面的函数式接口派上用场了,过滤还是不过滤呢,用户知道,只需要给我一个接口,剩下的就不管了。

map(Function<T, R> mapper):映射操作,至于怎么映射,用户知道,我关心的只是转换这个动作

flatMap(Function<T, ? extends Stream<? extends R>> mapper):这个方法的本质是map的一个特例,经过处理的T用户去处理,但是要求用户处理过后的T一定转换成Stream对象。还是只关心转换的这个行为,不考虑细节。

另外像reduce(归纳)、distinct(去重)、sorted(排序,默认自然顺序)、peek(偷看,实际上就是对流中当前的元素进行Consumer,不会对其造成影响)、limit(限制流的执行次数)、skip(从当前流中的位置跳过几个)、forEach、collect、count、parallel(并行流,基于fork-join)、sequential(串行流)

collect方法单独说一下,这是核心中的核心,无敌的操作。

本质上就是Collector接口在背后进行一系列的骚操作:

我推荐大家去把Collector接口的所有注释都读一遍,这里只截取重点:

Collector接口是一个可变的收集操作,他会把输入的元素累积到一个可变的结果容器中,在处理完所有输入元素之后,可以选择将累积的结果转换为最终表示,收集操作可以按顺序或并行执行
Collector接口的实现必须遵守如下约束:1.传递给accumulator函数,combiner函数,finisher函数的参数必须是上次对应函数调用所产生的结果;2.接口的实现不应该对supplier方法,accumulator方法,combiner方法的结果做任何操作,除了把这些结果传递给这些函数的下一次调用;3.如果一个结果传递给combiner或者finisher函数,那么相同的对象不会再一次返回;4一旦一个结果传递给combiner或者finisher函数,那么这个结果不会传递给accumulator函数;5.对于非并发集合,supplier函数,accumulator函数,combiner函数所返回的任何结果必须在一个连续的线程围栏里(各自处于各自线程的上下文);6对于并发集合,可以和非并发集合那样去用。但是也可以让多个线程公用一个结果容器,而且必须要有UNORDERED这个特征值。
T,流中待处理的元素;A,中间结果容器(通常隐藏实际类型细节);R,最终结果容器;A和R有时的类型可能一样,也可能不一样

Collector<T, A, R>

Supplier<A> supplier();

        supplier方法就是用户自定义的中间结果提供者

BiConsumer<A, T> accumulator();

        把T累加到A中

BinaryOperator<A> combiner();

        把上次的A合并到新的A中,并且返回新的A

Function<A, R> finisher();

        把A转换成R,返回R

Set<>Characteristics characteristics();  // 下面具体解释

Collector特征:

enum Characteristics {

1.表示这个collector是并发的,那意味着结果容器可以支持accumulator函数被多个线程并发调用,使用同一个容器;2.如果collector有CONCURRENT特征,但是没有UNORDERED特征,那么他只能被用在无序的数据源中

    CONCURRENT,

表示收集操作不承诺保存输入元素的顺序,前提是结果容器没有内在的顺序

    UNORDERED,

表示finisher函数为恒等函数,可以省略。如果设置此特征,必须保证A强制类型转换到R时成功的

    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

    新生成一个对象

关于异常处理

大家肯定有这种苦恼,宝宝好不容易学会了函数式接口,熟练运用Lambda表达式,但是这异常咋抛出不去呢。原因很简单,因为JDK8定义的函数式接口统统没有加上 throw Exception 啊!那么怎么解决呢。

我们自定义函数式接口呗,无非在JDK8的接口的基础上加上 throw Exception 不就好了么,但是事情往往不那么容易。首先,JDK8的Stream,Optional等等都是要基于JDK8本身的接口,我们自己写了一套那么我们自己用的爽,他本身的库函数就不爽了,再加上网络上其他的框架,不就没法兼容了么。

所以我们只能封装聚合一层,把JDK8的函数式接口来一个“倒卖二手货操作”:

实际上是JDK8的Consumer我们new出来返回,它会回过头来调用我们定义的接口。然后我们自己定义的Consumer用来抓捕异常。
因为JDK8的函数式接口抛不出去,所以只能通过方法来在运行时抛出。
使用的时候就可以在方法上跑出去了,然后其他的地方统一管理方法上的异常哦!

实例代码中已经把43个函数式接口全部提供了一份unchecked的版本了哦,欢迎大家来star!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容