前言
在对 Java8
发布的 Lambda
表达式进行一定了解之后,发现 Lambda
最核心的就是结合 Stream API
对集合数据的遍历、提取、过滤、排序等一系列操作的简化,以一种函数式编程的方式,对集合进行操作
lambda
表达式就是函数接口类型的实例
lambda
语法
基本语法:
(parameters) -> expression 或 (parameters) ->{
statements; }
基本语法的具体示例:
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
lambda
语法使用
1.用 lambda
表达式实现 Runnable
lambda
表达式替换了原来匿名内部类
的写法,没有了匿名内部类繁杂的代码实现,而是突出了,真正的处理代码。最好的示例就是实现 Runnable
的线程实现方式了: 用 () -> {}
代码块替代了整个匿名类
public class LambdaTest1 {
public static void main(String[] args) {
// 使用匿名内部类 实现多线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类 实现多线程");
}
}).start();
// 使用的lambda表达式语法为:() -> 5
new Thread(() -> System.out.println("使用lambda表达式")).start();
}
}
结果
匿名内部类 实现多线程
使用lambda表达式
2.lambda
表达式实现 forEach
遍历集合
实体类
public class Person {
private String name;
private int age;
private String sex;
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
// set,get 方法省略
}
2.1.使用语法: x -> 2 * x
进行 forEach
遍历
public class LambdaTest3 {
@NotNull
public static List<Person> getPersonList() {
Person p1 = new Person("liu", 22, "male");
Person p2 = new Person("zhao", 21, "male");
Person p3 = new Person("li", 18, "female");
Person p4 = new Person("wang", 21, "female");
List<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
return list;
}
public static void main(String[] args) {
List<Person> personList = LambdaTest3.getPersonList();
// 使用 lambda 表达式 forEach 遍历
personList.forEach(person -> System.out.println(person.toString()));
}
}
结果
Person{
name='liu', age=22, sex='male'}
Person{
name='zhao', age=21, sex='male'}
Person{
name='li', age=18, sex='female'}
Person{
name='wang', age=21, sex='female'}
2.2.forEach
遍历的语法解析(重点)
personList.forEach(person -> System.out.println(person.toString()));
查看 forEach
的源码,我们发现接收的参数类型为:Consumer<? super T> action
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
因此可推断 lambda
表达式的类型:
Consumer<Person> action = person -> System.out.println(person.toString());
如何理解这句代码呢?先看 java.util.function.Consumer
的源码:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
...(省略default方法)
}
我们发现它是一个函数接口,通俗的话讲:函数接口是指只含有一个抽象方法的接口,在 Java8
中,接口里面可以存在已实现的方法,如:default
默认方法、static
静态方法
注意:@FunctionalInterface
注解的作用,当函数接口中存在两个或两个以上的抽象方法时,编译会报错
所以,我们可以这样理解:person -> System.out.println(person.toString())
就是实现了 Consumer
接口中 accept
方法的实例,->
前面的部分 e
表示 accept
方法的入参,->
后面的部分表示 accept
方法的具体实现
注意:
- 如果方法的实现部分由多行代码组成,就用
{}
括起来 - 如果方法需要返回值,而且方法体只有一行代码,可以省略
return
。如果方法体有多行代码,则不能省略return
2.3.使用双冒号 ::
进行 forEach
遍历
public class LambdaTest3 {
@NotNull
public static List<Person> getPersonList() {
Person p1 = new Person("liu", 22, "male");
Person p2 = new Person("zhao", 21, "male");
Person p3 = new Person("li", 18, "female");
Person p4 = new Person("wang", 21, "female");
List<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
return list;
}
public static void main(String[] args) {
List<Person> personList = LambdaTest3.getPersonList();
// 使用 lambda 表达式 forEach 遍历
Consumer<Person> consumer = e -> e.setAge(e.getAge() + 3);
personList.forEach(consumer);
personList.forEach(System.out::println);
}
}
结果
Person{
name='liu', age=25, sex='male'}
Person{
name='zhao', age=24, sex='male'}
Person{
name='li', age=21, sex='female'}
Person{
name='wang', age=24, sex='female'}
2.4.双冒号::
(方法引用)的语法解析(重点)
personList.forEach(System.out::println);
之前说过,lambda
表达式就是函数接口类型的实例,而创建该实例的过程,需要实现函数接口中的抽象方法。因此,可以采用引用的方式,即引用已定义好的方法去实现函数接口中的方法
那么 System.out::println
该如何理解?同样根据上下文推断 lambda
表达式的类型:
Consumer action = System.out::println;
System.out::println
的函数接口类型为 java.util.function.Consumer
,即在产生该实例对象时,引用了 System.out
对象中的 println
方法,去实现了 Consumer
接口中的 accept
抽象方法。简而言之,即:引用 println
方法去实现了接口中的抽象方法
方法引用有很多种,下面主要讲述以下几种:
- 构造方法引用:
Class::new
- 静态方法引用:
ClassName::methodName
- 实例上的实例方法引用:
instanceReference::methodName
- 类型上的实例方法引用:
ClassName::methodName
文章未完成,后续更新