lambda表达式学习和应用

Lambda表达式

Lamdba 表达式是Java 8 的新特性,也是Java 8 中最重要的的新功能。Lamdaba表达式促进了Java使用函数方式进行编程。

在进行语法的讲解之前,需要了解和Lambda息息相关的Function Interface,可以说只有函数式接口的存在,才有lambda表达式的存在,换句话可以说lambda表达式是函数式接口的实现.

函数式接口

Function 接口具有单个功能,也就是有单个抽象方法.如 Comparable 接口只有一个方法 compareTo 用来进行比较。

Java8定义了函数接口被用来拓展Lamdba表达式。在 java.util.Function 包下有非常多的函数接口。

常用的接口包括;

接口 抽象方法 用法
Function<T,R> R apply(T t); 接受T类型参数,返回R类型结果
Consumer void accept(T t); 接受T类型参数,不返回结果
Predicate boolean test(T t); 接受T类型参数,返回Boolean结果
Suppler T get(); 无参数,返回T类型结果

定义函数接口

  1. 使用 @FunctionalInterface 注解,该注解不是必须的,但是如果标注,则编译器会检查这个接口下是否只有单个抽象方法,如果不是会报错.
  2. 新增default方法,是为了在现有的类库中中新增功能而不影响他们的实现类
  3. 新增接口内static方法,可定义一个或者多个静态方法,和普通的静态方法没有区别,都是接口名.方法名进行调用
  4. 在函数式接口的定义中是只允许有且只有一个抽象方法(必须),但是可以有多个static方法和default方法。

Lambda语法

一个Lambda表达式有如下:

1
paramter -> expression body

以下是lambda表达式的几个特性

  • 可选的类型描述 - 不需要声明参数的类型,编译器可根据参数的值进行推断
  • 可选的参数两旁的括号 - 声明单个参数的时候不需要使用括号,但是如何声明多个参数,括号还是必须的。
  • 可选的大括号 - 如果函数体中只有单行,则不需要在函数体两侧添加大括号
  • 可选的返回值 - 如果函数中只有单行,编译器自动返回这个单行的返回值

下面根据实例来看上面的四个特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Java8Tester {

public static void main(String args[]) {
GreetingService greetService1 = message -> System.out.println("Say:"+message);
greetService1.sayMessage("Hello!!");
MathOperation add = (int a,int b) -> a+b;
MathOperation sub= (a,b) -> a-b;
MathOperation muti = (a,b) -> {return a*b;};
System.out.println("add 函数结果:"+add.operation(2,2));
}

interface GreetingService {
void sayMessage(String message);
}

interface MathOperation{
int operation(int a,int b);
}
}
// 结果:
Say:Hello!!
add 函数结果:4

通过上面的例子,可以得出以下重要的两点:

  • Lambda 表达式主要用来定义功能接口的内联实现,如:一个接口里只有一个接口方法。在上面的例子里面我们使用了对MathOperation接口进行了多种实现。
  • Lambda 表达式消除了对匿名类的需求,如new Thread(new Runnable{..}) ,为Java提供了非常简单而强大的函数编程能力。

使用方法引用

方法引用(Method Refrences)帮助通过方法名称指向方法。方法引用使用符号“::”表示。方法引用可以用爱指向如下类型的方法:

  • 静态方法
  • 实例方法
  • 构造方法使用new关键字(TreeSet::new)

[方法引用]的格式是 类名::方法名

1
Arrays.stream(objectArray).forEach(System.out::println);

使用Lambda

熟能生巧,常常练习肯定能记住

1. 创建Thread

1
2
3
4
5
6
7
8
9
10
11
// 不使用Lambda创建Thread
// 可以看出内部直接创建了Runnable的匿名类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("新建线程。。");
}
}).start();

// 使用lambda创建Thread
new Thread(() -> System.out.println("使用Lambda新建线程。。")).start();

2. 列表迭代

1
2
3
4
5
6
7
8
9
10

List<Integer> list = Arrays.asList(1, 2, 3);
// 不使用Lambda
for (Integer integer : list) {
System.out.println(integer);
}
//使用Lambda
list.forEach((s) -> System.out.println(s));
//方法引用
list.forEach(System.out::println);

3. Map&Reduce

  • 使用Map处理列表内所有元素全部加2
1
2
3
4
5
6
7
// 不使用Lambda
for (Integer integer : list) {
int order = integer + 2;
System.out.println(order);
}
// 使用Lambda
list.stream().map((a) -> a + 2).forEach(System.out::println);
  • 使用 Map + collect 完成对每个元素计算后返回新的结果列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
List<Integer> newList = new ArrayList<>();

// 不使用Lambda
for (Integer integer : list) {
int order = integer + 2;
int result = order / integer;
newList.add(result);
}
for (Integer integer : newList) {
System.out.println(integer);
}
// 使用Lambda
newList = list.stream().map((a) -> (a + 2)/a).collect(Collectors.toList());
newList.forEach(System.out::println);

4. 使用filter实现元素的过滤

使用lambda,可直接使用filter进行元素的过滤

1
2
3
4
5
6
7
8
9
10
11
12
// 不使用lambda时
List<Integer> list_1 = new ArrayList<>();
for (Integer integer : list) {
if(integer != 3){
list_1.add(integer);
}
}


// 使用 filter 进行过滤
List<Integer> list_2 = list.stream().filter((a) -> a!=3).collect(Collectors.toList());
list_2.forEach(System.out::println);

5. 执行函数

稍复杂的字符串处理,用lambda合适不过了,下面的例子,只将特定字符的字符串进行 toUpperCase

1
2
3
4
5
6
7
8
9
10
List<String> s = new ArrayList<>();
s.add("hello ");
s.add("world ");
s.add("!! ");

List<String> s_1 = s.stream()
.filter(a -> a.startsWith("h") || a.startsWith("w"))
.map(String::toUpperCase)
.collect(Collectors.toList());
s_1.forEach(System.out::println);

6. 算数运算

支持使用min、max等直接实现Comparator进行运算,如:

1
Optional<Integer> min = list.stream().min(Integer::compareTo);

也支持使用 IntSummaryStatistics 状态,如:

1
2
3
4
5
IntSummaryStatistics intSummaryStatistics = list.stream().mapToInt(x -> x).summaryStatistics();
System.out.println("最大值:"+intSummaryStatistics.getMax());
System.out.println("最小值:"+intSummaryStatistics.getMin());
System.out.println("平均值:"+intSummaryStatistics.getAverage());
System.out.println("数量:"+intSummaryStatistics.getCount());

7. 自定义方法使用Function Inteface实现复杂功能

Java 8 提供了三个常用的函数接口包括;

  • Function<T,R>
  • Predicate 判断
  • Consumer 消费

通过定义方法使用这三类接口,能完成更复杂的调用逻辑,在这里仅仅举一个小例子🌰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List<Integer> functionMethods2(List<Integer> a, Function<List<Integer>, List<Integer>> function) {
a = a.stream()
.filter(demoFilter(2))
.collect(Collectors.toList());
return function.apply(a);
}

Predicate<Integer> demoFilter(Integer number) {
return (a) -> !a.equals(number);
}

// 客户端调用
JavaTester javaTester = new JavaTester();
List<Integer> resList = javaTester.functionMethods2(list, a -> {
a.add(2);
return a;
});
-------------本文结束感谢您的阅读-------------

本文标题:lambda表达式学习和应用

文章作者:NanYin

发布时间:2020年03月23日 - 19:03

最后更新:2020年03月23日 - 20:03

原始链接:https://nanyiniu.github.io/2020/03/24/Lambda%20expression/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。