Java8新特性-Lambda表达式

发布时间:2024年01月11日

java8 新特性

Lambda表达式

Lambda是一个匿名函数, 可以把lambda表达式理解为是一段可以传递的代码,(将代码像数据一样传递)

?// 比较两个整数的大小------采用匿名内部类的方式
?@Test
? ? ?public void test1(){
? ? ? ? ?Comparator<Integer> comp = new Comparator<Integer>() {
? ? ? ? ? ? ?@Override
? ? ? ? ? ? ?public int compare(Integer o1, Integer o2) {
? ? ? ? ? ? ? ? ?return o1.compareTo(o2);
? ? ? ? ? ?  }
? ? ? ?  };
? ? ? ? ?System.out.println(comp.compare(9, 5));
? ? 
? ?  }
??
? 
? // 比较两个整数的大小------采用 lambda
? ? ? ? ?Comparator<Integer> comp2 = (a, b) -> a.compareTo(b);
? ? ? ? ?System.out.println(comp2.compare(10, 4));
??

变化 需求: 求 薪资高于5000的员工信息

?// 建立 员工类 Emp, 包含姓名 薪资,年龄
?import lombok.AllArgsConstructor;
?import lombok.Data;
?import lombok.NoArgsConstructor;
?import lombok.ToString;
??
?@NoArgsConstructor
?@AllArgsConstructor
?@Data
?@ToString
?public class Emp {
? ? ?private String name;
? ? ?private double salary;
? ? ?private int age;
?}
??
?//编写 测试类
?// 查询 薪资高于5000的员工信息
? private List<Emp> filterEmp(List<Emp> emps){
? ? ? ? ?List<Emp> list2 = new ArrayList<>();
? ? ? ? ?for (Emp emp: emps) {
? ? ? ? ? ? ?if(emp.getSalary()>5000){
? ? ? ? ? ? ? ? ?list2.add(emp);
? ? ? ? ? ?  }
? ? ? ?  }
? ? ? ? ?return list2;
? ?  }
??
?//查询 年龄大于30的员工信息
? private List<Emp> filterEmp(List<Emp> emps){
? ? ? ? ?List<Emp> list2 = new ArrayList<>();
? ? ? ? ?for (Emp emp: emps) {
? ? ? ? ? ? ?if(emp.getAge()>30){
? ? ? ? ? ? ? ? ?list2.add(emp);
? ? ? ? ? ?  }
? ? ? ?  }
? ? ? ? ?return list2;
? ?  }
?// 查询 年龄小于20 或 查询 薪资低于7000的, 发现 每变更一次需求, 总是要 编写一个新的方法, 而且 大部分代码相同
?// 只有关键条件的代码不同, 因此 造成了 数据的 大量冗余,  那 如何改进呢?
??
?// 利用所学--- 采用设计模式改进, 定义接口, 接口里定义抽象方法 返回boolean,采用不同的实现类来操作, 每个需求都是不同的实现类,
?/**
? ? ?定义接口 MyEmp<T>  定义 方法 boolean test(T t);
?*/
?// 定义接口
?public interface MyEmp<T>{
? ? ?boolean test(T t);
?}
??
??
?//定义实现类-- 按照薪资过滤
?public class FilterBySalary implements MyEmp<Emp>{
? ? ?@Override
? ? ?public boolean test(Emp emp) {
? ? ? ? ?return emp.getSalary()>5000;
? ?  }
?}
??
?//定义实现类--按照年龄过滤
?public class FilterEmpByAge implements MyEmp<Emp>{
? ? ?@Override
? ? ?public boolean test(Emp emp) {
? ? ? ? ?return emp.getAge()>30;
? ?  }
?}
??
?//测试类中 , 编写方法, 以接口作为参数
? private List<Emp> filterEmp(List<Emp> emps, MyEmp<Emp> filter){
? ? ? ? ?List<Emp> list2 = new ArrayList<>();
? ? ? ? ?for (Emp emp: emps) {
? ? ? ? ? ? ?if(filter.test(emp)){
? ? ? ? ? ? ? ? ?list2.add(emp);
? ? ? ? ? ?  }
? ? ? ?  }
? ? ? ? ?return list2;
? ?  }
?// 测试方法 
? ? ?@Test
? ? ?public void test1() {
??
? ? ? ? ?//按照年龄过滤
? ? ? ? ?List<Emp> emps = filterEmp(list,new FilterEmpByAge());
? ? ? ? ?System.out.println(emps);
? ? ? ? ?//按照薪资过滤
? ? ? ? ?List<Emp> emps2 = filterEmp(list,new FilterBySalary());
? ? ? ? ?System.out.println(emps2);
? ?  }
??
?// 以上问题 可以得到解决, 但目前是采用了 设计模块(策略模式) 进行 代码优化,缺点: 就是每次都要编写实现类,怎么再次优化?
??
?//  优化上述问题,采用匿名内部类的方式,  只需要定义接口, 不需要 实现类
??
?@Test
? ? ?public void test1() {
??
??
? ? ? ? ?List<Emp> emps = filterEmp(list, new MyEmp<Emp>() {
? ? ? ? ? ? ?@Override
? ? ? ? ? ? ?public boolean test(Emp emp) {
? ? ? ? ? ? ? ? ?return emp.getSalary()>50000;
? ? ? ? ? ?  }
? ? ? ?  });
? ? ? ? ?System.out.println(emps);
??
? ? ? ? ?List<Emp> emps2 = filterEmp(list, new MyEmp<Emp>() {
? ? ? ? ? ? ?@Override
? ? ? ? ? ? ?public boolean test(Emp emp) {
? ? ? ? ? ? ? ? ?return emp.getAge()>30;
? ? ? ? ? ?  }
? ? ? ?  });
? ? ? ? ?System.out.println(emps2);
? ?  }
?// 以上代码 关键的就是比较那一句, 显得代码不简洁, 因此能不能再次优化? ? 可以使用  Lambda表达式
? @Test
? ? ?public void test1() {
??
? ? ? ? ?List<Emp> emps = filterEmp(list, emp->emp.getSalary()>5000);
? ? ? ? ?emps.forEach(System.out::println);
??
? ? ? ? ?List<Emp> emps2 = filterEmp(list, emp->emp.getAge()>40);
? ? ? ? ?emps2.forEach(System.out::println);
? ?  }
??
?// 以上代码 还有使用 stream 再次优化
?list.stream().filter(e->e.getSalary()>5000).forEach(System.out::println);
?list.stream().filter(e->e.getAge()>40).forEach(System.out::println);

Lambda基本语法

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

左侧: Lambda表达式的参数列表

右侧: Lambda表达式的所需执行的功能, 即Lambda体

语法格式一: 无参数 无返回值-------- 举例 Runnable 接口中的void run() 方法

示例: (注意: 不能在lambda内部 修改定义在域外的局部变量, 否则会 编译错误)

?Runnable r = ()->System.out.println("hello lambda");
?r.run();

语法格式二: 有一个参数, 无返回值 -- Consumer接口中 void accept(T t) 方法

示例:

?Consumer<String> str = x->System.out.println(x); // 箭头左侧 x的小括号省略
?str.accept("nihao");// nihao赋值给x, 并进行打印

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

语法格式四: 有两个以上的参数 有返回值,并且 Lambda体中 有多条语句 , 则 {} 不可以省略--- Comparator接口 int compare(int x,int y)

举例:

?Comparator<Integer> comp = (x,y)->{
? System.out.println("语句1");
? return Integer.compare(x,y);
?};
?System.out.println(comp.compare(5, 7));

语法格式五:有两个以上的参数 有返回值,并且 Lambda体中 有一条语句 , 则 {} 和return 均可以省略

示例: Comparator<Integer> comp = (x,y)-> Integer.compare(x,y);

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

List<String> list = new ArrayList<>(); // 等号右侧 的 <> 里面不需要写类型了, 这也是 类型推断 , 1.7会报错

总结: 左右遇一 括号省, 左侧推断类型省,

函数式接口

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

什么是函数式接口?

接口中只有一个抽象方法的接口 称为 函数式接口 ,可以使用 @FunctionalInterface 修饰, 检查是否是 函数式 接口

Lambda表达式练习题

1.调用Collections.sort()方法,通过定制排序比较两个Emp(先按年龄比,年龄相同 按姓名比),使用 Lambda 作为参数传递。 Emp.java 中 包含 姓名, 年龄 字段

?@Test
? ? ?public void test(){
? ? ? ? ?List<Emp> list = Arrays.asList(
? ? ? ? ? ? ? ? ?new Emp("王丽",3000,50),
? ? ? ? ? ? ? ? ?new Emp("李四",5000,50),
? ? ? ? ? ? ? ? ?new Emp("王丽2",3000,60),
? ? ? ? ? ? ? ? ?new Emp("李四2",3000,50)
? ? ? ?  );
? ? ? ? ?Collections.sort(list,(e1,e2)->{
? ? ? ? ? ? ?if(e1.getAge()==e2.getAge()){
? ? ? ? ? ? ? ? ?return e1.getName().compareTo(e2.getName());
? ? ? ? ? ?  }else{
? ? ? ? ? ? ? ? ?return Integer.compare(e1.getAge(),e2.getAge());
? ? ? ? ? ?  }
? ? ? ?  });
??
? ? ? ? ?list.forEach(System.out::println);
? ?  }
??

2.①声明函数式接口,接口中声明抽象方法,public String getValue(String str); ②声明类 TestLambda ,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值。 ③再将一个字符串的第2个和第4个索引位置进行截取子串。

?// 定义函数式 接口
?@FunctionalInterface
?public interface MyTestLambda {
? ? ? String getValue(String str);
?}
??
?// 定义测试类- 并添加 方法
?// 对s 执行 lambda的操作
? public String change(String s, MyTestLambda lambda){
? ? ? ? ?return lambda.getValue(s); //调用接口方法
? ?  }
??
?// 测试类-- 测试方法
? ? ?@Test
? ? ?public void test2(){
? ? ? ? ?//转大写
? ? ? ? ?String s = change("abc123",x->x.toUpperCase());
? ? ? ? ?System.out.println(s);
? ? ? ? ?// 提取 索引2-4 之间的内容
? ? ? ? ?String s1 = change("abc123",x->x.substring(2,5));
? ? ? ? ?System.out.println(s1);
??
? ?  }

3.①声明一个带两个泛型的函数式接口,泛型类型为<T, R> T为参数,R为返回值 ②接口中声明对应抽象方法。 ③在 TestLambda类中声明方法,使用接口作为参数,计算两个long型参数的和。④再计算两个long型参数的乘积。

?// 定义接口
?@FunctionalInterface
?public interface MyFun<T,R> {
? ? ?R change(T t1,T t2); ?// 因为要求对两个数 进行计算
?}
??
?// 定义 测试类-- 添加方法 , 对 t1, t2 两数 做 lambda的操作
? public Long change3(Long t1,Long t2,MyFun<Long,Long> lambda){
? ? ? ? ?return lambda.change(t1,t2);
? ? }
?//定义测试类- 测试方法
? @Test
? ? ?public void test3(){
? ? ? ? ?//计算两数 和
? ? ? ? Long sum = change3(5l,10l,(x,y)->x+y);
? ? ? ? ?System.out.println(sum);
? ? ? ? ?//计算两数 积
? ? ? ? ?Long sum2 = change3(5l,10l,(x,y)->x*y);
? ? ? ? ?System.out.println(sum2);
? ?  }
??

本次练习发现 每次都需要自定义接口,实际上新特性已经提供了函数式接口,下节课学习下

文章来源:https://blog.csdn.net/ly121862/article/details/135521399
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。