java8中一个非常重要的特性就是lambda表达式,我们可以把它看成是一种闭包,它允许把函数当做参数来使用,是面向函数式编程的思想,一定程度上可以使代码看起来更加简洁。例如以前我们使用匿名内部类来实现代码:
//匿名内部类写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("内部类写法");
}
}).start();
使用lambda则更加简洁:
//lambda 写法
new Thread(() -> System.out.println("lambda写法")).start();
1、lambda表达式语法
1.1lambda表达式的一般语法
(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
statment1;
statment2;
//.............
return statmentM;
}
这是lambda表达式的完全式语法,后面几种语法是对它的简化。
1.2单参数语法
param1 -> {
statment1;
statment2;
//.............
return statmentM;
}
当lambda表达式的参数个数只有一个,可以省略小括号
例如:将列表中的字符串转换为全小写
List<String> proNames = Arrays.asList(new String[]{"Ni","Hao","Lambda"});
List<String> lowercaseNames1 = proNames.stream().map(name -> {return name.toLowerCase();}).collect(Collectors.toList());
1.3单语句写法
param1 -> statment
当lambda表达式只包含一条语句时,可以省略大括号、return和语句结尾的分号
例如:将列表中的字符串转换为全小写
List<String> proNames = Arrays.asList(new String[]{"Ni","Hao","Lambda"});
List<String> lowercaseNames2 = proNames.stream().map(name -> name.toLowerCase()).collect(Collectors.toList());
1.4方法引用写法
表达式:
Class or instance :: method
例如:将列表中的字符串转换为全小写
List<String> proNames = Arrays.asList(new String[]{"Ni","Hao","Lambda"});
List<String> lowercaseNames3 = proNames.stream().map(String::toLowerCase).collect(Collectors.toList());
2、lambda表达式可使用的变量
将为列表中的字符串添加前缀字符串:
String waibu = "lambda :";
List<String> proStrs = Arrays.asList(new String[]{"Ni","Hao","Lambda"});
List<String>execStrs = proStrs.stream().map(chuandi -> {
Long zidingyi = System.currentTimeMillis();
return waibu + chuandi + " -----:" + zidingyi;
}).collect(Collectors.toList());
execStrs.forEach(System.out::println);
输出:
lambda :Ni -----:1474622341604
lambda :Hao -----:1474622341604
lambda :Lambda -----:1474622341604
变量waibu :外部变量
变量chuandi :传递变量
变量zidingyi :内部自定义变量
lambda表达式可以访问给它传递的变量,访问自己内部定义的变量,同时也能访问它外部的变量。
不过lambda表达式访问外部变量有一个非常重要的限制:变量不可变(只是引用不可变,而不是真正的不可变)。当在表达式内部修改waibu = waibu + " ";时,IDE就会提示你:Local variable waibu defined in an enclosing scope must be final or effectively final.编译时会报错。因为变量waibu被lambda表达式引用,所以编译器会隐式的把其当成final来处理。以前Java的匿名内部类在访问外部变量的时候,外部变量必须用final修饰。现在java8对这个限制做了优化,可以不用显示使用final修饰,但是编译器隐式当成final来处理。
3、lambda表达式中的this概念
在lambda,this不是指向lambda表达式产生的那个对象,而是声明它的外部对象。例如:
public class WhatThis {
public void whatThis(){
//转全小写
List<String> proStrs = Arrays.asList(new String[]{"Ni","Hao","Lambda"});
List<String> execStrs = proStrs.stream().map(str -> {
System.out.println(this.getClass().getName());
return str.toLowerCase();
}).collect(Collectors.toList());
execStrs.forEach(System.out::println);
}
public static void main(String[] args) {
WhatThis wt = new WhatThis();
wt.whatThis();
}
}
输出:
com.wzg.test.WhatThis
com.wzg.test.WhatThis
com.wzg.test.WhatThis
ni
hao
lambda
4、方法引用
本人认为是进一步简化lambda表达式的声明的一种语法糖。
前面的例子中已有使用到: execStrs.forEach(System.out::println);
4.1 对象::实例方法,将lambda的参数当做方法的参数使用
objectName::instanceMethod
示例:
Consumer<String> sc = System.out::println;
//等效
sc.accept("618, 狂欢happy");
Consumer<String> sc2 = (x) -> System.out.println(x);
4.2 类::静态方法,将lambda的参数当做方法的参数使用
ClassName::staticMethod
示例:
//ClassName::staticMethod 类的静态方法:把表达式的参数值作为staticMethod方法的参数
Function<Integer, String> sf = String::valueOf;
//等效
Function<Integer, String> sf2 = (x) -> String.valueOf(x);
String apply1 = sf.apply(61888);
4.3 类::实例方法,将lambda的第一个参数当做方法的调用者,其他的参数作为方法的参数
ClassName::instanceMethod
示例:
//ClassName::instanceMethod 类的实例方法:把表达式的第一个参数当成instanceMethod的调用者,其他参数作为该方法的参数
BiPredicate<String, String> sbp = String::equals;
//等效
BiPredicate<String, String> sbp2 = (x, y) -> x.equals(y);
boolean test = sbp.test("a", "A");
5、构造函数
无参的构造方法就是类::实例方法模型,如:
Supplier<User> us = User::new;
//等效
Supplier<User> us2 = () -> new User();
//获取对象
User user = us.get();
当有参数时:
//一个参数,参数类型不同则会编译出错
Function<Integer, User> uf = id -> new User(id);
//或加括号
Function<Integer, User> uf2 = (id) -> new User(id);
//等效
Function<Integer, User> uf3 = (Integer id) -> new User(id);
User apply = uf.apply(61888);
//两个参数
BiFunction<Integer, String, User> ubf = (id, name) -> new User(id, name);
User 狂欢happy = ubf.apply(618, "狂欢happy");
6、总结
java8引入lambda表达式是接收了函数式编程语言的思想,例如scala之类的,它将函数视为一等公民,可以使用高阶函数等。
和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。
和过程化编程相比,函数式编程里函数的计算可随时调用。
写在最后,lambda表达式可以使代码看起来简洁,但一定程度上增加了代码的可读性以及调试的复杂性,所以在使用时应尽量是团队都熟悉使用,要么干脆就别用,不然维护起来是件较痛苦的事。