知识精解系列将以博主的第一视角对各类技术中的核心要点进行深入的解析,不会覆盖相关技术的全部内容,适合想要快速了解相关技术核心内容的人群阅读。
本文为 《Java8函数式编程》(Java 8 Lambdas: Functional Programming for the Masses) 第二章内容的精解,若对相关知识感兴趣,想要全面的学习相关知识,推荐购书深入阅读。若认为文章涉嫌侵权,请联系作者及时删除。
本作品采用 知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议 (CC BY-NC-SA 3.0 CN) 进行许可 。非商业性质转载请注明作者和出处,禁止商业性质转载。
开源创造世界
有关Lambda表达式,写在前面
为了学习Java8Lambda表达式的相关内容,我也在网上翻阅许久,发现了一个共通的问题在于:有关Lambda表达式,大家都在强调它是原来版本的Java中匿名方法的一种替代。有过Java知识系统学习的读者的确知道什么是匿名方法,但是大部分的开发者在日常开发中并不经常使用。而且作为一种曾经Lambda表达式的替代方法,匿名方法的存在本身就是一个比较抽象且难以理解的东西。所以我并不赞同各位把Lambda表达式与以前的匿名方法扯上关系,尝试使用匿名方法的思想去理解Lambda表达式究竟是什么,我们只需要知道他是一个没有名字的方法就可以,不需要与过去的写法做类比。在我学习相关知识的时候就是把有关匿名方法与Lambda表达式的联系完全抛开之后,才恍然大悟的。当然这也可能因为我比较笨吧。
在我看来,我们可以把Lambda表达式做一个更容易理解的描述(注意是描述,而不是定义):
- Lambda表达式是没有名字的方法
- Lambda表达式可以当作方法的参数和返回值
我们完全可以把Lambda表达式整体当作一个Java中的实体来看待,就像一个变量、一个引用一样。
lambda表达式的几种写法
以下写法中,等号右边的内容就是Lambda表达式。
1.不包含参数,使用空括号代表没有参数。
Runnable noArguments = () -> System.out.println("Hello World");
2.只包含一个参数,可以省略参数的括号。
ActionListener oneArgument = event -> System.out.println("button clicked");
3.lambda表达式的主体是一个代码块
Runnable multiStatement = () - > {
System.out.println("Hello");
System.out.println("World");
};
4.包含多个参数
BinaryOperator<Long> add = (x,y) -> x + y;
5.显式声明参数类型
BinaryOperator<Long> addExplicit = (Long x,Long y) -> x + y;
可以看出,Lambda表达式和普通的方法在功能上别无二致。可以有0或多个入参,可以有0或1个返回值。可以输出内容,可以执行计算,等等。区别在于,它有一些简便的写法,包括去掉一些不必要的符号,去掉一些可以确定的类型限定符号。
目标类型:lambda 表达式所在上下文环境的类型。
Lambda 表达式的类型依赖于上下文环境,是编译器推断出来的。
小结:lambda 表达式中,-> 符号前面的内容对应于传统方法中的参数列表,后面的内容对应于方法体。而参数类型和返回值类型是编译器推断出来的。
引用值,而不是变量
在匿名内部类中,只能使用所在方法内的final变量。Java 8 放宽了这一限制,可以引用非final变量,但是该变量在既成事实上必须是final,Lambda表达式中的规则与之相同。
既成事实上的final(Effctively Final)是指只能给该变量赋值一次,也就是说,Lambda表达式引用的是值,而不是变量。 ——这是一个十分重要的概念,希望读者能够在此留心,深入理解一下,确保明白了其中真正的含义。
Lambda表达式的这种表现,也是其称之为闭包的原因。未赋值变量与周边环境隔离起来,进而被绑定到一个特定的值。
小结:lambda表达式又称之为闭包,表达式内只能引用 既成事实上的final变量。
函数接口
函数接口是只有一个抽象方法的接口,用作Lambda表达式的类型
Java中重要的函数接口
接口 | 参数 | 返回类型 | 用途 |
---|---|---|---|
Predicate | T | boolean | 断言 |
Consumer | T | void | 消费 |
Function | T | R | 转换 |
Supplier | None | T | 生产 |
UnaryOperator | T | T | 一员运算 |
BinaryOperator | (T,T) | T | 多员运算 |
小结:函数接口是只有一个抽象方法的接口,用作lambda表达式的类型。JDK 8 中提供了几种常用的函数接口。这几种接口在Java8的其他特性尤其是Stream API中将会经常被提到,请多看几遍,已经记住了这几种函数接口的作用。
类型推断
Lambda表达式中的类型推断,源自于Java 7 中已经引入的目标类型推断的扩展,Java7中的目标类型推断体现在菱形运算符上。
类型推断,程序依然要经过类型检查来保证运行的安全性,但不用再显式声明类型。
章节要点
- Lambda表达式是一个匿名方法,将行为像数据一样传递
- Lambda表达式的常见结构:BinaryOperator add = (x, y) -> x + y
- 函数接口指仅具有单个抽象方法的接口,用来表示Lambda表达式的类型。
Lambda表达式应该怎么使用,使用Lambda表达式能给我们带来怎么样的好处?我觉得大部分人解释的并不是很清楚,Lambda表达式自身的出现除了使代码变得简洁一些以外,似乎并不能给我们带来什么。其实不然,Lambda表达式的强大之处在于它和其他类库之间的配合,以及它解锁了一种新的编程思想,这背后的价值是无穷的。
在之后的Stream API相关的内容中,你会真切的体会到Lambda表达式的强大,令人惊叹。