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语法格式如下,如果一个表达式无法完整表达逻辑,那就就写成一个代码块,用花括号括起。
(参数...) -> [表达式;代码块]
示例:
//表达式 (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类型的构造函数。
在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);迟来的特性,哈哈,不过还是带来了很多便利的。
