Lambda是一段可以传递的代码,可以被多次执行。当然,不使用Lambda表达式也可以写出同样的代码,对比如下,可以看到lambda的写法简洁很多。
public interface Invocable<T,R> {
R invoke(T input);
}
public static void main(String[] args) {
myInvoke("str", new Invocable<String, String>() {
@Override
public String invoke(String input) {
return "MyInvocable#invoke --> " + input;
}
});
myInvoke("str", (input) -> "MyInvocable#invoke --> " + input);
}
private static void myInvoke(String str, Invocable invocable) {
System.out.println(invocable.invoke(str));
}
Lambda语法
Lambda语法格式如下,如果一个表达式无法完整表达逻辑,那就就写成一个代码块,用花括号括起。
(参数...) -> [表达式;代码块]
示例:
//表达式
(String first, String second) -> Integer.compare(first.length(), second.length());
//代码块
(String first, String second) -> {
if (first.length() < second.length()) return -1;
else if (first.length() > second.length()) return 1;
else return 0;
}
//无入参
() -> {
for (int i = 0; i < 10; i++) doSomeThing();
}
//自动推导入参类型
Comparator comp = (first, second) -> Integer.compare(first.length(), second.length());
函数式接口
对于只包含一个抽象方法的接口,就可以通过lambda表达式来创建该接口的对象啦,这种接口就叫做函数式接口。java.util.function包中预定义了好多函数式接口,并且同意标记上了注解【@FunctionalInterface】。自定义的函数式接口也建议加上这个注解,因为编译器会检查该实体,是否只包含一个抽象方法的,同时也更加清晰可读。
例如:
Map<Integer, User> userMap = Stream.of(new User(0, "张三", 18, 0), new User(0, "张四", 19, 0), new User(0, "老张", 20, 1))
.collect(Collectors.toMap(User::getId, item -> item));
这里的toMap方法接收的输入参数就是Function函数:
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
其他常见预定义函数的说明可以参见上一篇文章(http://www.atatech.org/articles/61907)
方法引用和构造器引用
方法引用:
//对象::实例方法:
instance::method --> (input1,input2...) -> instance.method(input1,input2...)
//类::静态方法:
class::staticMethod --> (input1,input2...) -> class.staticMethod(input1,input2...)
//类::实例方法:
class::method --> (input1,input2,input3...) -> input1.method(input2,input3...)
构造器引用:
List<String> list = Arrays.asList("a", "b");
list.stream().map(MyClass::new);
构造器引用会自动适配一个合适的构造函数,例如上面的示例会适配一个入参为一个String类型的构造函数。
Lambda的闭包语法糖
在java中原有的闭包:
public static Supplier<Integer> testClosure() {
final int i = 1;
return new Supplier<Integer>() {
@Override
public Integer get() {
return i;
}
};
}
public interface Supplier<T> {
T get();
}
此处变量i是testClosure方法的内部变量,但是我们在匿名内部函数get中却返回了 i;内部变量 i 的生存周期延长了,并且使得变量 i 可以被外部函数所引用,这就是闭包。注意变量 i 必须为final,否则会编译错误。
在Lambda中的闭包:
public static Supplier<Integer> testClosure() {
int i = 1;
//i++;
return () -> {
return i;
};
}
此处 i 不需要定义为final,但是这仅仅是一个语法糖,事实上任何对 i 的value的变更一样会引起语法错误。
延迟执行
lambda表达式都是延迟执行的,如果需要立即执行一段代码,就没必要使用lambda表达式了,就好像如果需要立即执行就没必要使用匿名内部类一样。lambda表达式可以在另一个线程中执行,可以多次运行,按需运行,在某个正确的时机(事件触发,算法运行的某个时间点)运行。
例如,常用的log记录方式如下:
log.info("x:" + x + ",y:" + y);
参数会立即计算完成再传递给info方法,此时会判断日志级别,如果符合info输出才会打印日志,如果为debug等info不输出的情况,会忽略当前信息,但是呢参数已经计算过了。做了一次无用功。此时如果改为lambda延迟执行,则不会出现这种无谓的计算。如下:
log.info(() -> "x:" + x + ",y:" + y);
总结
迟来的特性,哈哈,不过还是带来了很多便利的。