前言
Lambda 表达式是JDK8中一个重要的特性,它使用一个清晰简洁的表达式来表达一个接口,同时Lambda表示式也简化了对集合以及数组数据的遍历、过滤和提取等操作。
一、Lambda 出现的背景
回想一下在java8之前,我们要将某些功能传递给某个方法,要怎么做?
比如我们现在要实现对一个数组元素的降序排序,有以下几种做法
1.单独写一个类实现Comparator接口
public class StringCompartor implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
}
使用的时候把这个类的对象传入
public class Lambda {
public static void main(String[] args) {
String[] arr = {"a", "b", "d", "c"};
Arrays.sort(arr, new StringCompartor());
System.out.println(Arrays.toString(arr));
}
}
2.使用内部类
public class Lambda {
public static void main(String[] args) {
String[] arr = {"a", "b", "d", "c"};
Arrays.sort(arr, new StringCompartor());
System.out.println(Arrays.toString(arr));
}
static class StringCompartor implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
}
}
3.使用匿名内部类
如果我们只想用一次这个功能,我们可以不用单独写个类,使用匿名内部类的方式也可以完成
public class Lambda {
public static void main(String[] args) {
String[] arr = {"a", "b", "d", "c"};
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
System.out.println(Arrays.toString(arr));
}
}
4.Lambda 表达式
即使匿名内部类已经帮助我们简化了很多工作,但是编写起来还不是很方便,于是lambda表达式就出现了
同样的例子代码如下:
public class Lambda {
public static void main(String[] args) {
String[] arr = {"a", "b", "d", "c"};
Arrays.sort(arr,(o1,o2)->{return o2.compareTo(o1);});
System.out.println(Arrays.toString(arr));
}
}
使用它可以写出更简洁, 更灵活的代码。作为一种更紧凑的代码风格,使java语言的表达式能力得到的提升。
二、Lambda 概述
Lambda 表达式是一个匿名函数,我们可以把Lambda表达式理解为一段可以传递的代码(将代码段像数据一样传递)。
语法糖(Syntactic Sugar),也称糖衣语法,指在计算机语言中添加的某种语法,这种语法对语言本身功能来说没有什么影响,只是为了方便程序员的开发,提高开发效率。
Lambda 表达式的本质只是一个"语法糖",由编译器推断并帮你转换包装为常规的代码,更少的代码实现相同的功能。
注意:这种表达式只针对有一个抽象方法的接口实现,以简洁的表达式形式实现接口功能来作为方法参数
一个Lambda表达式由三个部分组成,分别为参数列表、-- >、和表达式主体
(参数列表):向表达式主体传递接口方法需要的参数,多个参数用英文逗号进行分隔
->:表示Lambda表达式箭牌,用来指定参数数据指向,必须用英文横线和大于号书写
{表达式主体}:由单个表达式或语句块组成的主体,本质就是接口中抽象方法的具体实现。
三、函数式接口
虽然Lambda表达式可以实现匿名内部类的功能,但在使用时有一个局限,即接口中有且仅有一个抽象方法的接口。
什么是函数式接口?
函数式接口是 java 8 中的新增功能,它们只允许一个抽象方法,使用@FunctionalInterface注解
接下来通过一个案例来演示函数式接口的定义与使用
//定义无参,无返回值的函数式接口
@FunctionalInterface
interface Animal{
void shout();
}
//定义有参、有返回值的函数式接口
interface Calculate{
int sum(int a,int b);
}
public class Example {
public static void main(String[] args) {
//分别两个函数式接口进行测试
animalShout(()-> System.out.println("无参、无返回值的函数式接口调用"));
showSum(10,20,(x,y)->{ return x+y;});
}
//创建一个动物叫的方法,并传入接口对象Animal作为参数
public static void animalShout(Animal animal){
animal.shout();
}
//创建一个求和的方法,并传入两个int类型以及接口Calculate类型的参数
public static void showSum(int x,int y,Calculate calculate){
System.out.println(x+"+"+y+"的和为:"+calculate.sum(x,y));
}
}
注意:函数式接口只能有一个抽象方法。如果我们尝试在其中添加一个抽象方法,则会抛出编译时错误
@FunctionalInterface注解只是显式地标注了接口是一个函数式接口,并强制编辑器进行严格的检查,该注解对程序运行并没有实质上的影响。
编译器会将所有满足函数式接口要求的接口视为函数式接口,而不管这个接口声明中是否使用了函数式接口的注释(即@FunctionalInterface,例如上面例子的Calculate接口,没有用注解,但也是个函数式接口)。
函数式接口的疑惑
为什么Comparator接口有两个抽象方法compare和equals,Comparator还是一个函数式接口?
根据Java语言规范的定义,一个使用了该注释的接口类型声明将被视为一个函数式接口。从概念上讲,一个函数式接口有且只有一个抽象方法。由于默认方法已经有了实现,所以它们不是抽象方法。如果一个接口中声明的抽象方法是重写了超类Object类中任意一个public方法,那么这些抽象方法并不会算入接口的抽象方法数量中。因为任何接口的实现都会从其父类Object或其它地方获得这些方法的实现。
重写了超类Object类中任意一个public方法的方法并不算接口中的抽象方法!!!
所以虽然Comparator接口中有两个抽象方法compare和equals,但equals并不算入接口中的抽象方法(重写了object的equals方法),所以Comparator接口还是满足函数式接口的要求,Comparator接口是一个函数式接口。
四、Lambda 示例
线程初始化
//1.匿名内部类方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello,World");
}
}).start();
//2.lambda表达式方式
new Thread(() -> {
System.out.println("Hello,World");
}).start();
事件处理
Button botton=new Button();
//1.匿名内部类方式
botton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hello,World");
}
});
//2.lambda表达式方式
Button button2=new Button();
button2.addActionListener((e)->{
System.out.println("Hello,World");
});
遍历
List list=new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
for(Object t:list){
System.out.println(t);
}
//lambda表达式方式
list.forEach(t-> System.out.println(t));
总结
Lambda表达式是函数式编程的体现,只有确保接口中有且仅有一个抽象方法,Lambda表达式才能顺利地推导出所实现的这个接口中的方法。以后的章节会学习到Stream接口,该接口可以将集合、数组中的元素转换为Stream流的形式,并结合Lambda表达式的优势进一步简化操作,。