如何使用lambda
首先明确两个概念:
1.什么是lambda?
lambda表达式本质上是一种函数式接口的创新写法,它没有名称,但是有参数列表,函数主体,返回类型.lambda最终表示的是该接口的一个对象.
2.函数式编程是否脱离了面向对象的思想?
函数式编程通常指代使用lambda方式进行编程.而我们传递lambda本质上还是在传递一个函数式接口的实现对象.从这个角度上来说lambda并没有脱离面向对象的编程思想.但是从编程风格上来说,当我们在使用lambda时,我们的目的是传递函数的实现.所以lambda可以认为是面向对象特色的函数式编程.
如何编写lambda
首先来一个栗子:
(Apple a1, Apple a2) -> a1.getWehght().compareTo(a2.getWeight());
这段lambda "->" 前面表示的是参数部分,后面表示的是函数主体,以及隐形的int类型返回值.
我们在前面介绍了传递lambda本质上还是再传递一个函数式接口实现的对象,所以这段lambda就可以负值给我们实现对象的类,即:
Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWehght().compareTo(a2.getWeight());
而不使用lambda表达式前的写法是这样的
Comparator<Apple> byWeight = new Comparator<Apple>(){
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWehght());
}
};
是不是感觉简洁了很多,你可能会很好奇,lambda写法如此简洁,省略了很多部分,那lambda是怎么被映射到我们想要实现的类的.实际上这里涉及lambda的另一个特性,就是必须被用在函数是接口.
函数式接口:该接口只定义了一个抽象方法
另外java会通过上下文匹配你想要实现的函数式接口以及是否对应到该函数式接口的实现,来定性lambda的具体语义.
所以上面这段lambda在实际使用中是这样的:
/**对list中的元素按照重量进行排序**/
list.sort((Apple a1, Apple a2) -> a1.getWehght().compareTo(a2.getWeight()));
如此,我们便不用再费力再自己去实现一个Comparator或者写一大串匿名类实现了.是不是简洁了很多.
java8中提供的通用函数式接口
1.Predicate
内部定义了一个test方法,接收泛型T对象,并返回boolean类型.
2.Consumer
定义了accept方法,接收泛型T对象,没有返回值.
3.Function
定义了apply方法,接收泛型T对象,并返回一个泛型R对象.
这种通用型函数式接口省去了我们再使用lambda时,还需要自己去写一个特定接口的麻烦,除了这三个之外另外java还提供了一些函数式接口,能满足我们大多数场景对传递lambda的需要.
关于上面三种函数式接口的lambda实现示例如下
/**Predicate**/
(int t) -> t == 1;
/**Consumer**/
(Apple a) -> a.setName("xiao apple");
/**Function**/
(String s) -> s.length();
看到这里我们是不是已经学会如何使用lambda了?实际上上述代码还可以继续简化,在java8中编译器会从上下文推断出用什么函数式接口来配合lambda表达式,这意味着它也可以推断出适合lambda的签名,这样我们可以在lambda中省去标注的参数类型,同样举本文中第一个栗子,我们可以进一步简化成:
/**lambda会根据上线问需要的类型自动推断出a1, a2匹配的类型**/
(a1, a2) -> a1.getWehght().compareTo(a2.getWeight());
写到最后:
lambda虽然能一步步简化我们代码的写法,但是也让我们理解代码的上下文越来越难,因为它本身就存在一些需要推断的逻辑.试想一下如果代码中到处都是最后这个例子的写法,那我们理解和修改这个程序会多么困难.所以我建议使用lambda以及我们后面还会讲到的函数引用和stream api,最好都用在特定场景,例如集合排序,筛选等,这样既可以简化我们代码的实现,提升编程效率,特定场景又能让我们比较方便的理解.