如何理解 Java8 的函数式编程,面试必进

Java8 出现的时间已经不算短了,免费维护期马上也要到期了,官方已经开始推荐使用 Java11。

Java8 是革命性的一个版本,一直以来,Java 最受诟病的地方就是代码写起来很啰嗦,仅仅写一个 HelloWorld 都需要些很多的样板代码。

Java8 推出之后,啰嗦的代码有了很大的改观,Java 也可以写出简单优美的代码。最明显的改观就是 Java 开始支持函数式编程。

函数式编程的定义很晦涩,但是我们可以将函数式编程理解为函数本身可以作为参数进行传递,就是说,参数不仅仅可以是数据,也可以是行为(函数或者方法的实现其实就是逻辑行为)。

可能是 Java8 步子跨的太大,以至于现在还有很多人没有赶上来,依然用 Java8 在写 Java5 风格的代码。

这篇文章的目的就是彻底说清楚 Java8 的变化,以及快速全面的使用 Java8 的特性,让 Java 代码优雅起来。

Java面试突击、阿里巴巴Java面试笔记、面试答案关注我私信回复【444】获得免费获取方式!

函数式接口

在开始说 Java8 的函数式编程之前,我们需要说明一下,在 Java8 中新增加的一个概念,叫函数式接口

这个函数式接口是 Java8 实现函数式编程的基础,正是这类接口的存在,才能把函数(方法)当做参数进行传递,至少表面上看起来是这样的,但是实际上传递的还是对象,这个问题我们下面再讨论,先回到函数式接口。

下面就是一个函数式接口:

publicinterfaceAction{publicvoidaction();}

这个函数式看起来和普通的接口没有什么区别,唯一的区别是函数式接口只能有一个抽象方法

如果你想让别人立马理解这个接口是函数式接口,可以加上 @FunctionalInterface 注解,这个注解不会提供任何额外的功能,仅仅用来表示这个接口是一个函数式接口。

@FunctionalInterfacepublicinterfaceAction{publicvoidaction();}

只能有一个抽象方法是为了更方便的把函数作为参数来传递,这个后面再细说。

我们可以根据自己的需要来定义函数式接口,JDK 为了使用方便,内置了很多函数式接口,日常使用完全够了。

常用的函数接口有:

1.Function

2.Predicate

3.Consumer

函数式接口其实就这么简单,看到这里你可能还是对函数式接口不是很理解,没关系,现在你仅仅只需要记住函数式接口就是模板

Lambda 表达式

说起 Java8 的函数式编程,很多人都知道 lambda 表达式,这也是 Java8 中最容易被人记住的地方。

先来通过一个直观的例子来了解一下 lambda,在操作 ArrayList 等数据结构时,我们有可能要对其中的数据进行排序,比如:

Comparator comparator =newComparator() {publicintcompare(Integer i1, Integer i2){returni1.comparaTo(i2);    }}

在上面的代码中,真正有用的代码也有比较大小的那行,其他的都是样板代码。在这样的情况下,lambda 就很有用。

Comparator comparator =(Integer i1, Integer i2)->{returni1.compareTo(i2);}

这样看起来是不是很简单了,但是还是继续优化,可以把返回参数的部分也省略:

Comparator comparator =(Integer i1, Integer i2)->i1.compareTo(i2)

既然两个参数都是 Integer 那是不是也可以省略,最后就得到了下面这样的形式:

Comparator comparator =(i1, i2)->i1.compareTo(i2)

这就是 lambda 的力量,可以把上面那么多的代码浓缩成一行。

lambda 其实就是一段代码,但也不仅仅是一段代码,再简单的 lambda 也会有三部分,参数列表,箭头和 lambda 主体,上面的 (i1, i2) 就是参数列表, i1.compareTo(i2) 就是 lambda 主体,箭头把这两部分隔开了。

lambda 是匿名的,这点和 Java 中的匿名实现类有点像(本质上一样),而且它是一种函数,不属于任何类(属于类的函数称之为方法),并且可以作为参数进行传递,而且还很简洁。

看到这里,可能就有人猜到函数式接口和 lambda 之间可能有某些关系了,没错,我们已经快说到最重要的部分。

在这之前再来理解一个概念,函数签名,函数签名为可以表示一类函数,如果两个函数的以下部分相同,就可以说这两个函数的签名一致:

函数参数及其类型

返回值及其类型

可能会抛出的异常

还有访问控制符(public 等等)

最关键的地方来了,只要 lambda 和函数式接口方法的签名一致,lambda 表达式就可以作为参数传入到以该函数式接口为参数类型的方法中

来看个详细的例子, Comparator 接口的定义如下:

@FunctionalInterfacepublicinterfaceComparator{intcompare(T o1, T o2);}

虽然 Comparator 中方法不止一个,但是抽象方法只有 compare 一个,上面的 lambda 完全可以作为 compare 方法的实现,实际上,lambda 表达式确实是作为函数式接口抽象方法的实现,而且,lambda 表达式为作为整个函数接口的实例

到这里,真相大白,Java 8 的虽然支持了函数式编程,这不代表函数就是 Java 中的一等公民了,每一个函数其实还是被包裹成一个对象,对象依然是 Java 中的一等公民。

所以简单来说,只要 lambda 的表达式的参数和返回类型可以与函数式接口中的抽象方法对的上,lambda 就可以作为该函数式接口的实现进行传递。

比如上面列举的几种函数式接口,其实就是对一些通用函数的抽象,比如 Function 函数式接口如下:

@FunctionalInterfacepublicinterfaceFunction{Rapply(T t);}

这种就代表接受一个参数,返回另一个值的函数,只要满足这个要求的 lambda 表达式都可以作为 它的实现。

再比如 Predicate 接口,代表接受一个参数返回一个布尔值的函数:

@FunctionalInterfacepublicinterfacePredicate{booleantest(T t);}

Comsumer 接口表示接受一个参数,什么都不返回的函数:

@FunctionalInterfacepublicinterfaceConsumer{voidaccept(T t);}

如果这样理解起来还是有点困难,那就把这些函数式接口理解为 lambda 表达式的类型。

类型检查和类型推断

在上面我们说到了只要函数式接口抽象方法的函数签名与 lambda 一致,那么就可以把 lambda 表达式作为该函数式接口的实现。

上面的例子中, lambda 的参数类型也是可以省略的,那么 Java 是如何判断 lambda 是否与函数式接口匹配呢?

如果 lambda 表达式中,参数和返回值的类型都省略之后,需要从使用 lambda 的上下文推断出来。

方法引用

本来到这里应该就很完美了,lambda 够简洁,用它写代码,可以省略很多无用的样本代码,但是 lambda 也不完美,因为 lambda 表达式的代码很难复用,而且很多的 lambda 表达式仅仅就是调用了其他的方法。

这个时候,方法引用就可以派上用场了,比如上面的例子,其实仅仅就是调用了 Integer.compareTo() 方法:

Comparator comparator =(i1, i2)->i1.compareTo(i2)

还可以简化成下面的样子:

Comparatorcomparator = Integer::compareTo

使用方法引用的时候,要使用 :: ,而且任何方法都可以这样被引用,无论是静态方法还是实例方法。

方法引用可以被认为是 lambda 的语法糖,使用方法引用可以让代码更加简洁,更直观,看到方法引用的名称就能大概知道代码的逻辑,并且还可以对一些代码进行复用。

写出 Java8 风格的代码

在 Java8 之后,很多代码的写法应该摒弃,下面列举一些常见的例子。

遍历 List

Java8 以前:

for(Integeri : list) {System.out.println(i);}

Java8 及以后:

list.forEach(System.out::println);

forEach 接收 Consumer 类型的函数,而 System.out.println() 刚好就符合要求。

遍历 Map

Java8 以前:

for(Map.Entryentry : map.entrySet()) {System.out.println("Key: "+ entry.getKey() +", Value:"+ entry.getValue()); }

Java8 及以后:

map.forEach((k, v)->{System.out.println("Key: "+ k +", Value: "+ v)});

这里的 forEach 不是接收 Consumer 类型的函数,而是接收 BiConsumer 类的函数,可以用来处理 map 这种包含 key-value 类型的数据,在大多数场景下,内置的函数式接口以及足够我们使用,只有在一些特殊的场景下才需要自己定制。

这里也没有现成的方法引用,所以就可以使用 lambda 表达式来实现。

执行异步任务

假如要使用线程池来执行任务。

Java8 以前:

ThreadPoolExecutor executor =newThreadPoolExecutor(10,10,0, TimeUnit.SECONDS,newArrayBlockingQueue(2000));executor.submit(newRunnable() {publicvoidrun(){        System.out.println("Thread pool execute");    }});

Java8 以后:

ThreadPoolExecutor executor =newThreadPoolExecutor(10,10,0, TimeUnit.SECONDS,newArrayBlockingQueue(2000));executor.submit(()->{    System.out.println("Thread pool execute");});

也可以使用 lambda 来解决这个问题,Runnable 也是一个函数式接口。

Java面试突击、阿里巴巴Java面试笔记、面试答案关注我私信回复【444】获得免费获取方式!












作者【Rayjun】

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

推荐阅读更多精彩内容