Java 在最开始是不支持函数式编程的,想来也好理解,因为在 Java 中类 Class 才是第一等公民,这就导致在 Java 中实现编程不是件那么容易的事儿,不过虽然难,但是结果我们也已经知道了,在 Java 8 这个大版本里为了支持函数式编程,Java 引入了很多特重要特性,咱们在前面几篇文章中,分别学习了其中的 Lambda 表达式和 Stream API 里的各种流操作,今天这篇文章我们再来梳理一下 Java 内置给我们提供的函数式接口。
本文大纲如下:
Java 根据常用需求场景的用例,抽象出了几个内置的函数式接口给开发者使用,比如Function
、 Supplier
等等,Stream 中各种操作方法的参数或者是返回值类型往往就是这些内置的函数式接口。
比如 Stream 中 map 操作方法的参数类型就是 Function
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
复制代码
那为什么我们在平时使用 Stream 操作的 map 方法时,从来没有见过声明这个类型的参数呢?大家可以回顾一下我们 Stream API 操作那一篇文章里使用 map 方法的例子,比如下面这个通过 map 方法把流中的每个元素转换成大写的例子。
List<String> list = new ArrayList<String>();
Stream<String> stream = list.stream();
Stream<String> streamMapped = stream.map((value) -> value.toUpperCase());
复制代码
map 方法的参数直接是一个 Lambada 表达式:
(value) -> value.toUpperCase()
复制代码
这个Lambda 表达式就是Function
接口的实现。
函数式接口的载体通常是 Lambda 表达式,通过 Lambda 表达式,编译器会根据 Lambda 表达式的参数和返回值推断出其实现的是哪个函数式接口。使用 Lambda 表达式实现接口,我们不必像匿名内部类那样--指明类要实现的接口,所以像 Stream 操作中虽然参数或者返回值类型很多都是 Java 的内置函数式接口,但是我们并没有显示的使用匿名类实现它们。
虽然Lambda 表达式使用起来很方便,不过这也从侧面造成了咋一看到那些 Java 内置的函数式接口类型时,我们会有点迷惑“这货是啥?这货又是啥?”的感觉。
下面我们先说一下函数式编程、Java 的函数式接口、Lambda 为什么只能实现函数式接口这几个问题,把这些东西搞清楚了再梳理 Java 内置提供了哪些函数式接口。
函数式编程
函数式编程中包含以下两个关键的概念:
- 函数是第一等公民
- 函数要满足一下约束
- 函数的返回值仅取决于传递给函数的输入参数。
- 函数的执行没有副作用。
即使我们在写程序的时候没有一直遵循所有这些规则,但仍然可以从使用函数式编程思想编写程序中获益良多。
接下来,我们来看一下这两个关键概念再 Java 函数编程中的落地。
函数是一等公民
在函数式编程范式中,函数是语言中的第一等公民。这意味着可以创建函数的“实例”,对函数实例的变量引用,就像对字符串、Map 或任何其他对象的引用一样。函数也可以作为参数传递给其他函数。
在 Java 中,函数显然不是第一等公民,类才是。所以 Java 才引入 Lambda 表达式&