写点什么

Java8 之 Lambda 表达式

用户头像
hepingfly
关注
发布于: 2020 年 09 月 24 日
Java8 之 Lambda 表达式

一、为什么要使用 Lambda 表达式

简单来说,Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(可以像数据一样进行传递)。因此我们使用 Lambda 表达式就可以写出更简洁、更灵活的代码。



下面一起来探寻下为什么要使用 Lambda 表达式:



1、假如我现在想比较两个数的大小,那么我可以通过下面的方式去实现



// 匿名内部类的方式
@Test
public void test1() {
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
List<Integer> list = Arrays.asList(3,5,2);
Collections.sort(list,com);
System.out.println(list);
}
// Lambda表达式的方式
@Test
public void test2() {
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
List<Integer> list = Arrays.asList(3,5,2);
Collections.sort(list,com);
}

上面实现方式,第一种就是通过匿名内部类的方式去实现,第二种是通过 Lambda 方式去实现,我们可以发现使用 Lambda 的方式明显比匿名内部类的方式高效的很多。



2、假设我现在有个需求,想获取当前公司中员工年龄大于35的员工信息,传统的方法我们应该是这么做的:

List<Employee> employees = Arrays.asList(new Employee("张三", 24, 3200),
new Employee("李四", 32, 1300),
new Employee("王五", 60, 7000),
new Employee("赵六", 74, 8500),
new Employee("田七", 18, 2700));
public List<Employee> filterEmployee(List<Employee> list) {
List<Employee> emps = new ArrayList<Employee>();
// 对 list 集合进行遍历找出年龄大于 35 岁的
for (Employee employee : list) {
if (employee.getAge() > 35) {
emps.add(employee);
}
}
return emps;
}
//------------------------------------------------------------------
//假设我现在又有一个需求:获取当期公司中员工工资大于5000的员工信息
public List<Employee> filterEmployee2(List<Employee> list) {
List<Employee> emps = new ArrayList<Employee>();
for (Employee employee : list) {
if (employee.getSalary() > 5000) {
emps.add(employee);
}
}
return emps;
}

对比上面写的两个方法,我们可以发现, 第二个方法和第一个方法就一行代码不同,存在大量代码冗余。既然存在代码冗余,那么我们就要对代码进行优化:



优化方式一:

//策略模式
//我写一个接口
public interface MyPredict<T> {
public boolean test(T t);
}
public List<Employee> filterEmployee(List<Employee> emps, MyPredict<Employee> mp) {
// 我遍历emps集合,按照MyPredict接口的实现类的规则进行过滤
List<Employee> emplist = new ArrayList<Employee>();
for (Employee employee : emps) {
if (mp.test(employee)) {
emplist.add(employee);
}
}
return emps;
}
@Test
public void test4() {
// 这样做的好处就是你设计好的代码filterEmployee不用动,当你有一个新需求的时候,
//只需要新写一个类去实现MyPredict即可
List<Employee> list = filterEmployee(employees, new FilterEmployeeByAge());
// 这样就得到过滤后的list了
System.out.println("-----------------");
// 假如我现在想要按工资过滤,那么我只需要新写一个实现类去实现MyPredict接口即可
List<Employee> list2 = filterEmployee(employees, new FilterEmployeeBySalary());
}

上面的这种优化方式,你会发现如果你有很多需求的话,你就要新建很多的实现类,而这些实现类里面就一个方法,这样比较不划算,所以我们就考虑到使用匿名内部类的方式。所以第二种优化方案:



优化方式二:

@Test
public void test5() {
List<Employee> list = filterEmployee(employees, new MyPredict<Employee>() {
@Override
public boolean test(Employee t) {
return t.getSalary() < 6000;
}
});
}

直接使用匿名内部类的方式,避免创建很多的类。在匿名内部类的基础上,我们可以进一步使用 Lambda 表达式,所以第三种优化方案:



优化方式三:

@Test
public void test6() {
List<Employee> list = filterEmployee(employees, (e) -> e.getSalary() > 5000);
}

使用 Lambda 表达式一行代码解决。综上来看,使用 Lambda 表达式确实可以帮助我们写出更灵活,更简洁的代码。



二、Lambda 表达式语法



Java8 中引入了一个新的操作符 "->",该操作符称为箭头操作符或Lambda操作符,箭头操作符将 Lambda 表达式拆分成两部分:

  • 左侧: 指定了 Lambda 表达式所需要的所有参数

  • 右侧: 指定了 Lambda 体,即 Lambda 表达式要执行的功能



所谓的 Lambda 表达式其实就是对接口的实现,之前我们用匿名内部类去实现接口,现在我们就可以使用 Lambda 表达式去实现接口。



关于左侧和右侧到底写什么在说下: 接口中它是有一个抽象方法的,所谓的 Lambda 表达式左侧的参数列表对应的就是你接口中抽象方法的参数列表,相应的箭头操作符的右侧,就是你对接口中抽象方法的实现。



那假设接口中有多个抽象方法,Lambda 表达式到底实现哪一个呢?

答:实际上 Lambda 表达式需要函数式接口的支持。所谓的函数式接口就是你这个接口中只有一个抽象方法的接口。



Lambda 表达式语法格式:



语法格式一:无参数,无返回值

例:

() -> System.out.println("hello world");

@Test
public void test1() {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello world");
}
};
runnable.run();
Runnable run = () -> System.out.println("hello");
run.run();
}



语法格式二:有一个参数,并且无返回值

例:

(x) -> System.out.println(x);



语法格式三:若只有一个参数,小括号可以省略不写

例:

x -> System.out.println(x);

@Test
public void test2() {
Consumer<String> con = (x) -> System.out.println(x);
con.accept("我是 hepingfly");
}



语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句

例:

Comparator<Integer> com = (x,y) -> {

System.out.println("函数式接口");

return Integer.compare(x, y);

};



@Test
public void test3() {
//如果 Lambda 体有多条语句,那么必须用大括号括起来
Comparator<Integer> com = (x,y) -> {
System.out.println("欢迎关注 hepingfly");
return Integer.compare(x, y);
};
}



语法格式五:若 Lambda 体中只有一条语句,return 和大括号都可以省略不写

例:

Comparator<Integer> com = (x,y) -> Integer.compare(x, y);



语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为 JVM 编译器通过上下文推断出数据类型,即 "类型推断"

例:

(Integer x, Integer y) -> Integer.compare(x, y);

因为我左边参数列表写的是接口中抽象方法的参数列表,像上面 Comparator 接口,我在泛型中已经指定参数是 Integer 类型的,所以接口的抽象方法中参数列表肯定是 Integer 类型的。



@Test
public void test4() {
Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
}



注意:

  • Lambda 表达式需要函数式接口的支持

  • 函数式接口: 接口中只有一个抽象方法的接口,称为函数式接口。可以使用注解 @FunctionalInterface 修饰,检查是否是函数式接口,如果不是会编译不过。



发布于: 2020 年 09 月 24 日阅读数: 59
用户头像

hepingfly

关注

视频号:hepingfly 分享干货,欢迎关注~ 2018.06.23 加入

B站程序员。目标是做一个有才华,身上有光的男人。

评论

发布
暂无评论
Java8 之 Lambda 表达式