一. Java8特性详解之lambda表达式
- 前言
- 1、Lambda是什么?
- 2、Lambda如何使用?
- 1.使用之前写遍历
- 普通写法
- 优雅写法
- 2.使用之后写遍历
- lambda表达式和Stream的配合写法
- 3、Lambda解读
- 1. Lambda的简化写法
- 2. Lambda的外部引用
- 最后
前言
本文主要介绍Java8对屌丝码农最有吸引力的一个特性—lambda表达式。
1、Lambda是什么?
官方解释
a function (or a subroutine) defined, and possibly called, without being bound to an identifier。
其实通俗点解释:就是一段带有输入参数的可执行语句块,不用被绑定到一个标识符上,就可能被调用的函数。
2、Lambda如何使用?
1.使用之前写遍历
普通写法
List<String> markList = new ArrayList<>();
markList.add("A");
markList.add("B");
markList.add("C");
List<String> lowercaseMark = new ArrayList<>();
for (String mark : markList) {
lowercaseMark.add(mark.toLowerCase());
}
优雅写法
List<String> markList2 = new ArrayList<>();
markList2.add("A");
markList2.add("B");
markList2.add("C");
List<String> lowercaseMark2 = FluentIterable.from(markList2).transform(new Function<String, String>() {
@Override
public String apply(String mark) {
return mark.toLowerCase();
}
}).toList();
这两种编程风格称为命令式编程和声明式编程(自行百度),具体孰优孰劣不好说,如果能摒弃习惯的命令式编程,寻求声明式的写代码方式,归纳提取好的实现,我们会站在更高层面写代码。
2.使用之后写遍历
lambda表达式和Stream的配合写法
List<String> markList3 = new ArrayList<>();
markList3.add("A");
markList3.add("B");
markList3.add("C");
List<String> lowercaseMark3 = markList3.stream().map((String mark) -> {
return mark.toLowerCase();
}).collect(Collectors.toList());
//这段代码就是对一个字符串的列表,把其中包含的每个字符串都转换成全小写的字符串。注意代码中的map方法调用,这里map方法就是接受了一个lambda表达式(其实是一个java.util.function.Function的实例)。
这里先大致解释一下什么是Stream,具体下个章节讲
官方解释:
A sequence of elements supporting sequential and parallel aggregate operations.
//Stream是元素的集合,这点让Stream看起来用些类似Iterator;
//可以支持顺序和并行的对原Stream进行汇聚的操作;
其实通俗点解释:Stream可以当成一个高级版本的Iterator。Iterator用户只能一个一个的遍历元素并对其执行某些操作;而Stream用户只要给出需要对其包含的元素执行什么操作,具体这些操作如何应用到每个元素上,就给Stream就好了!
3、Lambda解读
1. Lambda的简化写法
(1)参数类型省略–绝大多数情况,编译器都可以从上下文环境中推断出lambda表达式的参数类型。这样lambda表达式就变成了:
(param1,param2, …, paramN) -> {
statment1;
statment2;
//…
return statmentM;
}
所以我们最开始的例子就变成了(省略了List的创建):
List<String> lowercaseMark3 = markList3.stream().map((mark) -> {
return mark.toLowerCase();
}).collect(Collectors.toList());
(2)当lambda表达式的参数个数只有一个,可以省略小括号。lambda表达式简写为:
param1 -> {
statment1;
statment2;
//…
return statmentM;
}
所以我们最开始的例子就变成了(省略了List的创建):
List<String> lowercaseMark3 = markList3.stream().map(mark -> {
return mark.toLowerCase();
}).collect(Collectors.toList());
(3)当lambda表达式只包含一条语句时,可以省略大括号、return和语句结尾的分号。lambda表达式简化为:
param1 -> statmentM
所以我们最开始的例子就变成了(省略了List的创建):
List<String> lowercaseMark3 = markList3.stream().map(mark ->
mark.toLowerCase()
).collect(Collectors.toList());
2. Lambda的外部引用
上面的例子,只看到可以访问给它传递的参数,也能自己内部定义变量。lambda表达式如何访问其外部变量?
String[] array = {"a", "b", "c"};
for(Integer i : Lists.newArrayList(1,2,3)){
Stream.of(array).map(item ->
Strings.padEnd(item, i, '@')
).forEach(System.out::println);
}
这个例子中,map中的lambda表达式访问外部变量Integer i。并且可以访问外部变量是lambda表达式的一个重要特性,这样我们可以看出来lambda表达式的三个重要组成部分:
输入参数
可执行语句
存放外部变量的空间
不过lambda表达式访问外部变量有一个非常重要的限制:变量不可变(只是引用不可变,而不是真正的不可变)。
String[] array = {"a", "b", "c"};
for(int i = 1; i<4; i++){
Stream.of(array).map(item ->
Strings.padEnd(item, i, '@')
).forEach(System.out::println);
}
上面的代码,会报编译错误。因为变量i被lambda表达式引用,所以编译器会隐式的把其当成final来处理。回忆一下以前java的匿名内部类在访问外部变量的时候,外部变量必须用final修饰。Bingo,在java8对这个限制做了优化,可以不用显示使用final修饰,但是编译器隐式当成final来处理。