文章目录
一、什么 Lambda 表达式
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样传递)。使用它可以写出更简洁、更灵活的代码。
Lambda 表达式的本质是函数式接口(只有一个抽象函数的接口)的实例。
二、Lambda 表达式使用
2.1 基本语法
接口<泛型> 对象 = (形参列表)-> {函数体}
形参列表:当只有没有形参时,可以用 () 代替;当只有一个形参时,可以省略 ();当接口中指明了泛型时,可以省略参数类型;
函数体:当函数体中只有一条语句时,大括号可以省略;当函数体中只有一条 return 语句时,return 关键字可以省略。
2.2 代码示例
下面代码,第一个是使用接口的匿名对象来实现 run 方法,第二个是使用 lambda 表达式实现的 run 方法。
public class LambdaTest1 {
@Test
public void test() {
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("使用接口的匿名实现类");
}
};
r1.run();
//
Runnable r2 = () -> System.out.println("使用 Lambda 表达式");
r2.run();
}
}
/**运行结果:
使用接口的匿名实现类
使用 Lambda 表达式
分析:
() -> System.out.println() 与
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("使用接口的匿名实现类");
}
};
是等价的,证明了Lambda 表达式的本质是函数式接口(只有一个抽象函数的接口)的实例
示例二:
public void test2() {
Comparator<Integer> comparator1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
System.out.println(comparator1.compare(1, 2));
//两个参数,多条语句的 lambda 表达式
Comparator<Integer> comparator2 = (x, y) -> {
System.out.printf("x = %d, y = %d \n", x, y);
return Integer.compare(x, y);
};
System.out.println(comparator2.compare(10, 15));
//两个参数,只有一个 return 语句的 lambda 表达式
Comparator<Integer> comparator3 = (x, y) -> Integer.compare(x, y);
System.out.println(comparator3.compare(10, 15));
}
三、Java 内置四大核心函数式接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZGgFKFL3-1586062710333)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200403000200251.png)]
3.1 练习使用 Consumer 接口
public void testConsumer() {
Consumer<String> consumer1 = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer1.accept("使用匿名实现类");
Consumer<String> consumer2 = s -> System.out.println(s);
consumer2.accept("使用 Lambda 表达式");
Consumer<String> consumer3 = System.out::println;
consumer3.accept("使用方法引用");
}
3.2 练习使用 Supplier 接口
public void testSupplier() {
Supplier<String> supplier1 = new Supplier<String>() {
@Override
public String get() {
return "使用匿名实现类";
}
};
System.out.println(supplier1.get());
Supplier<String> supplier2 = () -> "使用 Lambda 表达式";
System.out.println(supplier2.get());
}
3.3 练习使用 Function 接口
public void testFunction() {
Function<String, String> function1 = new Function<String, String>() {
@Override
public String apply(String s) {
return (s + ", 接口匿名实现");
}
};
System.out.println(function1.apply("hello"));
Function<String, String> function2 = s -> s + ", lambda 表达式";
System.out.println(function2.apply("hello"));
}
3.4 练习使用 Predicate 接口
public void testPredicate() {
//接口的匿名实现类
List<Integer> list = Arrays.asList(10, 5, 4, 3, 10, 20, 30);
Predicate<Integer> predicate1 = new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer >= 10;
}
};
List<Integer> res1 = filter(list, predicate1);
System.out.println(res1);
//lambda 表达式
Predicate<Integer> predicate2 = integer -> integer >= 10;
List<Integer> res2 = filter(list, predicate2);
System.out.println(res2);
}
private List<Integer> filter(List<Integer> list, Predicate<Integer> predicate) {
List<Integer> res = new ArrayList<>();
for (Integer i : list) {
if (predicate.test(i)) res.add(i);
}
return res;
}
四、方法引用
方法引用就是简化了的 lambda 表达式:如果 lambda 表达式中使用的形参数列表和 lambda 方法体中的函数使用的参数一样,那么就可以用方法引用来简化。
方法应用的语法:函数式接口 变量名 = 类名/对象 :: 方法名
例:
public void test1() {
//接口的匿名实现类
Comparator<Integer> comparator1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println(comparator1.compare(2, 3));
//lambda 表达式 1
Comparator<Integer> comparator4 = (o1, o2) -> Integer.compare(o1, o2); //Integer.compare(o1, o2) 中的 o1, o2 与前面的完全相同,所以可以使用方法引用
System.out.println(comparator2.compare(2, 4));
//方法引用 1
Comparator<Integer> comparator5 = Integer::compareTo;
System.out.println(comparator5.compare(2, 5));
//lambda 表达式 2
Comparator<Integer> comparator2 = (o1, o2) -> o1.compareTo(o2);//这样的情形也可以使用方法引用,因为和第一种情形是等价的
System.out.println(comparator2.compare(2, 4));
//方法引用 2
Comparator<Integer> comparator3 = Integer::compareTo;
System.out.println(comparator3.compare(2, 5));
}
例:不能改写成方法引用的情况
public void test2() {
Consumer<String> consumer1 = s -> System.out.println(s);
consumer1.accept("这种情况可以改写成方法引用"); //因为 accept() 中使用的参数与 println() 中使用的参数完全一致
Consumer<String> consumer2 = s -> System.out.println("不好意思" + s);
consumer1.accept("这种情况下就不能改写成方法引用"); //因为 accept 中使用的参数和 println() 中使用的参数不一致
}
五、Lambda 表达式的作用
从上面的几个例子看起来 Lambda 表达式除了在创建线程哪个例子里较为使用,其他的例子并不实用,但我们知道 Lambda 的实际作用是用来充当函数式接口的实例的,只要用得到函数式接口的地方,就能用 Lambda 表达式来简化
例:
public void test3() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
//一般的 for-each
for (Integer integer : list) {
System.out.println(integer);
}
//Lambda 表达式简化
list.forEach(System.out::println);
}
/** foreach 源码,参数是一个 Consumer 的函数式接口
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
*/
而且,Lambda 在 Stream API(Java 函数式编程的关键) 中会大量用到,因为 Stream API 中的绝大部分函数都有函数式接口类型的形参。
例:
public void test2(){
//得到一个 employee 的列表
List<Employee> employees = EmployeeData.getEmployees();
// 使用 stream api 来统计列表中薪资大于 5000 的员工个数,直接用一个 filter 就实现了
long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
}
/** filter 源码,参数是一个 Predicate 的函数式接口
Stream<T> filter(Predicate<? super T> predicate);
*/