1、背景
lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。lambda表达式就和方法一样样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式和一个代码块)。Lambda表达式,基于数学中的λ演算得名,也可成为闭包。
1.1 Lambda表达式的语法
基本语法:(parameters)-> expression 或 (parameters)->{statements;}
Lambda表达式由三部分组成:
1. paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。
2. ->:可理解为“被用于”的意思

1.2函数式接口
要了解lambda表达式,首先要了解函数式接口,函数式接口的定义:一个接口有且只有一个方法。
注意:
1、如果一个接口有且只有一个抽象方法,那么该接口是函数式接口。
2、保障该接口是函数式接口:如果我们在一个接口上声明了@FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果该接口中有两个抽象方法,程序编译就会报错的。
如图所示,为一个函数式接口:
如果多一个方法,此时注解就会报错。
我们之前在抽象类和接口-CSDN博客中讲到:在jdk1.8之后,接口中的default方法可以有具体实现,如果添加进去,该接口任然为函数式接口。
2、lambda表达式的基本使用
在我们之前的文章 Map&Set 4 面试题汇总 _map的面试题-CSDN博客中,有这么一道题:692. 前K个高频单词 - 力扣(LeetCode),它需要建一个小根堆,之前我们使用匿名内部类完成,下面,我们通过lambda表达式进行完成。
匿名内部类代码如下:
public class Test {public static void main(String[] args) {PriorityQueue<Map.Entry<String,Integer>> priorityQueue = new PriorityQueue<>(new Comparator<Map.Entry<String, Integer>>() {@Overridepublic int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {return o1.getValue()-o2.getValue();}});}
}
lambda表达式代码:
public class Test {public static void main(String[] args) {PriorityQueue<Map.Entry<String,Integer>> priorityQueue = new PriorityQueue<>((Map.Entry<String,Integer> o1,Map.Entry<String,Integer> o2) ->{return o1.getValue() - o2.getValue();});}
}
到了这里相信大家已经对lambda表达式有了初步认识,下面我们通过完成一些lambda表达式的练习,对lambda表达式的使用进行巩固。
首先先准备好如下函数式接口,然后尝试在main方法中使用lambda表达式完成这些接口
//无返回值无参数
@FunctionalInterface
//函数式接口
//只能有一个方法
interface NoParameterNoReturn {void test();
}
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {void test(int a);
}
//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {void test(int a,int b);
}
//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {int test();
}
//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {int test(int a);
}
//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {int test(int a,int b);
}
实现:
public static void main(String[] args) {NoParameterNoReturn noParameterNoReturn = () -> System.out.println("hello,world");OneParameterNoReturn oneParameterNoReturn = (a) -> System.out.println(a);MoreParameterNoReturn moreParameterNoReturn = (a, b) -> System.out.println(a+b);NoParameterReturn noParameterReturn = ()-> {return 0;};OneParameterReturn oneParameterReturn = (a) -> {return a;};MoreParameterReturn moreParameterReturn = (a, b) -> {return a+b;};}
测试:
2.1 语法精简
1、参数类型可以省略,如果需要省略,每个参数的类型都有省略。
2、参数的小括号里面只有一个参数,那么小括号可以省略。
3、如果方法体当中只有一句代码,则大括号可以省略
4、如果方法体中只有一条语句,而且是return语句,那么大括号可以省略,且去掉return关键字。
示例代码:
public static void main(String[] args) {// 1.参数类型可以省略,如果需要省略,每个参数的类型都有省略。MoreParameterNoReturn moreParameterNoReturn = (a,b) -> System.out.println(a+b);// 2.参数的小括号里面只有一个参数,那么小括号可以省略。OneParameterNoReturn oneParameterNoReturn = a -> {System.out.println(a);};// 3.如果方法体当中只有一句代码,则大括号可以省略NoParameterNoReturn noParameternoReturn = ()-> System.out.println("hello world");// 4.如果方法体中只有一条语句,而且是return语句,那么大括号可以省略,且去掉return关键字NoParameterReturn noParameterReturn = () -> 0;}
3、变量捕获
Lambda表达式中存在变量捕获,了解了变量捕获之后,才能更好的理解Lambda表达式的作用域。
3.1 匿名内部类的变量捕获
下面通过代码来展示匿名内部类中的变量捕获:
首先,有这么一个接口:
interface NoParameterNoReturn {void test();
}
实现的匿名内部类如下:
public static void main(String[] args) {int a = 10;new NoParameterNoReturn(){@Overridepublic void test() {System.out.println(a);}}.test();}
此时,点击运行,代码是能够正常执行的:
那如果我们在内部类中修改当前a的值为99,它还能够正常运行吗?
可以看到,程序报错。这就是匿名内部类的变量捕获,也就是说:传入匿名内部类中的量,必须是常量或者没有改变过值的量。
3.2 lambda表达式的变量捕获
lambda表达式也存在变量捕获机制,和上面的匿名内部类同理:
4、Lambda表达式在集合中的使用

以上方法的作用可自行查看java帮助手册,这里进行一些方法的使用示例。
注意:Collection的forEach方法是从接口java.lang.Iterable拿过来的。
4.1 List接口
foreach方法和Sort方法
public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("hello");list.add("abc");list.add("adc");System.out.print("排序前的结果: ");list.forEach((s -> System.out.print(s + " ")));System.out.println();System.out.print("排序后的结果: ");list.sort((o1,o2)->o1.compareTo(o2));list.forEach(s -> System.out.print(s + " "));}
4.2 Map接口
foreach方法
public static void main(String[] args) {Map<String,Integer> map = new HashMap<>();map.put("hello",4);map.put("abv",3);map.put("666",6);map.forEach((s, integer) -> System.out.println("key: "+s+" val: "+integer));}
5、总结
lambda表达式:
优点 :
1、代码简洁,开发迅速
2、方便函数式编程
3、非常容易进行并行计算
缺点:
1、代码可读性变差
2、在非并行计算中,很多计算未必有传统的for性能高
3、不容易进行调试