Java8——Lambda表达式

简介: 为什么使用Lamdba表达式

为什么使用Lamdba表达式

我来举一个范例就知道了!

使用匿名内部类

    public void test1(){
        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o2,o1);
            }
        };
    }

上面那么多的代码,其实就一句话有用Integer.compare(o2,o1)

使用Lambda表达式

   public void test2(){
        Comparator<Integer> com = (o1, o2) -> Integer.compare(o1, o2);
    }

是不是很方便了。但可能也会有人觉得并没有什么太大的简便。那么下面再给出一个例子来引入 Lambda 表达式。

创建 Employee 类,有name、age、salary三个属性

public class Employee {
    private String name;
    private int age;
    private double salary;

    public Employee() {
    }
    public Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    public double getSalary() {
        return salary;
    }

    @Override
    public String toString() {
        return "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary;
    }
}
  • 需求1:获取员工年龄大于35的员工信息

我们模拟一下数据库,所以准备一个 List 集合

//Arrays.asList 将数组转换成集合
List<Employee> employees = Arrays.asList(
    new Employee("张三",18,9999.99),
    new Employee("李四",38,5555.55),
    new Employee("王五",50,6666.66),
    new Employee("赵六",16,3333.33),
    new Employee("田七",8,7777.77)
);

既然来了需求那我们就要开发呀

  /**
     * 获取员工年龄大于35的员工信息
     * @param list
     * @return
     */
    public List<Employee> findEmployeesByAge(List<Employee>list){
        List<Employee>emps = new ArrayList<>();
        for(Employee emp : list){
            if(emp.getAge() > 35){
                emps.add(emp);
            }
        }
        return emps;
    }

并且来波开发自测

 @Test
    public void test3(){
        List<Employee> employeesByAge = findEmployeesByAge(employees);
        for (Employee employee : employeesByAge) {
            System.out.println(employee);
        }
    }

输出结果
name='李四', age=38, salary=5555.55
name='王五', age=50, salary=6666.66

完美!!!美滋滋。完成任务。然后又来了一个需求

  • 需求2:获取公司中工资大于5000的员工信息
 public List<Employee> findEmployeesBySalary(List<Employee>list){
        List<Employee>emps = new ArrayList<>();
        for(Employee emp : list){
            if(emp.getSalary() > 5000){
                emps.add(emp);
            }
        }
        return emps;
    }

你写完之后发现,特么...就改了个方法名和判断条件,聪明的你发现这代码不能这么写。为什么不能这么写呢!如果你牛逼的客户**,突然又来了一个需求,获取年龄大于20的,薪资大于3000的。这要怎么办。继续加方法?那肯定不行!

优化一:设计模式之策略模式

创建查询行为的接口

public interface MyPredicate<T> {
    public boolean test();
}

并创建相关的实现类代表不同的行为: (分别是年龄> 35和工资> 5000的 )

//获取员工年龄大于35的员工信息
public class FindEmployeesByAge implements MyPredicate<Employee>{
    @Override
    public boolean test(Employee emp) {
        return emp.getAge() > 35;
    }
}


//获取公司中工资大于5000的员工信息
public class FindEmployeesBySalary implements MyPredicate<Employee>{
    @Override
    public boolean test(Employee emp) {
        return emp.getSalary() > 5000;
    }
}

Test类中,给外部提供一个通用方法

public List<Employee> filterEmployees(List<Employee>list,MyPredicate<Employee>mp){
    List<Employee>emps = new ArrayList<>();
    for(Employee emp : list){
        if(mp.test(emp)){  //重点代码
            emps.add(emp);
        }
    }
    return emps;
}

写了这么久自测一下

  @Test
   public void test4(){
       // 传入 FindEmployeesByAge  获取员工年龄大于35的员工信息
       List<Employee> employees = filterEmployees(this.employees, new FindEmployeesByAge());
       for (Employee employee : employees) {
           System.out.println(employee);
       }
   }

输出结果
name='李四', age=38, salary=5555.55
name='王五', age=50, salary=6666.66

如果想要测试工资大于5000的员工信息,只需要换掉 filterEmployees(this.employees, new FindEmployeesByAge())中的FindEmployeesByAgeFindEmployeesBySalary

又觉得上面那种方式,来一个需求就需要创建一个实现类,那还有没有其他方式呢?

优化二:匿名内部类

好处:不需要创建接口的具体的实现类(但还是需要MyPredicate接口和filterEmployees()方法):

  @Test
    public void test5(){
        List<Employee> employees = filterEmployees(this.employees, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getAge() >= 18;
            }
        });
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }
输出结果
name='张三', age=18, salary=9999.99
name='李四', age=38, salary=5555.55
name='王五', age=50, salary=6666.66

优化三:Lambda表达式

哈哈哈,优化优化就回到了开头那个例子(*^▽^*),Lambda 表达式省去 匿名内部类 的没用的代码,提高了代码的整洁性和可读性(注还是需要那个filterEmployees方法 )

 @Test
    public void test6(){
        List<Employee> employees = filterEmployees(this.employees, (e) -> e.getAge() > 15);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }
输出结果
name='张三', age=18, salary=9999.99
name='李四', age=38, salary=5555.55
name='王五', age=50, salary=6666.66
name='赵六', age=16, salary=3333.33
将这一段代码
    new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getAge() >= 18;
            }
        });
简化为了:(e) -> e.getAge() > 15

优化四:使用 Stream API

使用该方式,不需要我们上面所创建的所有方法,包括接口、实现类。当然数据还是要的。(这里先提一句)

  @Test
    public void test7(){
        employees.stream()
                .filter((e) -> e.getAge() > 35)
                .forEach(System.out::println);
    }
输出结果
name='李四', age=38, salary=5555.55
name='王五', age=50, salary=6666.66

Lambda表达式基础语法

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

  • 左侧: Lambda 表达式的参数列表,也就是接口中抽象方法的参数列表
  • 右侧: Lambda 表达式中所需要执行的功能(即:Lambda体),对应的是对抽象方法的实现

注:Lambda表达式需要函数式接口的支持。所谓函数式接口就是接口中只能有一个抽象方法

基础语法

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

  @Test
    public void test1(){
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("普通 Runnable");
            }
        };
        runnable.run();

        System.out.println("=============我是分割线=============");

        Runnable runnable1 = () -> System.out.println("Lambda Runnable");
        
        runnable1.run();
    }

输出:
普通 Runnable
=============我是分割线=============
Lambda Runnable

格式二:一个参数、无返回值(参数列表中有且只有一个参数,那么小括号可以省略不写)


    @Test
    public void test2(){
        //Consumer 后面会介绍到
        //Consumer con = (x) -> System.out.println(x);
        Consumer con = x -> System.out.println(x);
        con.accept("NiuBi Class");
    }
输出:NiuBi Class

格式三: 多个参数,有返回值,Lambda 体只有一条语句(大括号和return可以省略)

@Test
public void test3(){
   Comparator<Integer> com = (Integer x,Integer y) -> Integer.compare(x, y);
   int compare = com.compare(12, 333);
   System.out.println(compare);

}
输出:-1

格式四:多个参数,有返回值,Lambda体有多条语句(要用大括号括起来,并且要写上return)

@Test
  public void test4(){
    Comparator<Integer>com = (Integer x,Integer y) -> {
        System.out.println("函数式接口");
        return Integer.compare(x,y);
    };
  // 执行com.compare这代码之后  Lambda体才被执行
    int compare = com.compare(1211, 333);
    System.out.println(compare);
}
输出:
函数式接口
1

格式五:类型推断

Lambda 表达式的参数列表的数据类型可以省略不写,因为 JVM 编译器可以通过上下文推断出数据类型,这过程称之为"类型推断",例如:(Integer x,Integer y ) -> Integer.compare(x,y); 可以简写成(x,y) -> Integer.compare(x,y);

函数式接口

  • 接口中只有一个抽象方法的接口称为函数式接口
  • 使用注解@FunctionlInterface来检查是否是函数式接口

接下来,我们来创建一个函数式接口, 对一个数进行 + - * / 的运算

@FunctionalInterface
public interface MyFun {
    Integer getValue(Integer num);
}

Test类增加通用方法:

 public Integer operation(Integer num,MyFun mf){
        return mf.getValue(num);
    }

测试:

 @Test
    public void test5(){
        System.out.println(this.operation(100,(x) -> x * x ));
    }

再提一嘴:

"->",该操作符称为箭头操作符或者Lambda操作符,箭头操作符将 Lambda 表达式拆分成两部分:

  • 左侧: Lambda 表达式的参数列表,也就是接口中抽象方法的参数列表。 Integer getValue(Integer num);,也就是 num
  • 右侧: Lambda 表达式中所需要执行的功能(即:Lambda体),对应的是对抽象方法的实现。相当于
  @Override
      public Integer getValue(Integer num) {
          return num * num;
      }

四大内置函数式接口

我们发现,如果使用 Lambda 还要自己写一个函数式接口的话太麻烦,所以 Java8 提供四大内置函数式接口,只有一小部分比较特殊的情况需要我们自己去定义函数式接口

  • Consumer<T> 消费型接口 void accept(T t); :对类型为T的对象应用操作
  • Supplier<T> 供给型接口 T get(); :返回类型为T的对象
  • Function<T, R> 函数型接口 R apply (T t);:对类型为T的对象应用操作,并返回结果,结果是R类型的对象
  • Predicate<T> 断言形接口 boolean test(T t);:确定类型为T的对象是否满足某约束,并返回boolean值,满足为 true

image.png

Consumer<T> 消费型接口 void accept(T t);

@Test
    public void test1() {
       //例子1
        happy(1000,(x)->System.out.println("你在我这消费了" + x + "元"));
        
        //例子2
        List<String> list = Arrays.asList("1", "2", "3");
        list.forEach((obj) -> System.out.println(obj));
    }

 public void happy(Integer i, Consumer<Integer> con){
     // 执行 Lamdba 体
       con.accept(i);
   }

Supplier<T> 供给型接口 T get();

需求: 产生指定个数的整数,并放入集合中

@Test
   public void test2(){
       List<Integer> numList = getNumList(5, () -> (int) (Math.random() * 100));
       numList.forEach((obj)->System.out.println(obj));
   }

    public List<Integer> getNumList(int num, Supplier<Integer> sup){
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            // 执行 Lamdba 体
            Integer value = sup.get();
            list.add(value);
        }
        return list;
    }

Function<T, R> 函数型接口 R apply (T t);

 @Test
    public void test3(){
        //去掉空格
        System.out.println(strHandler("    gongj   ",(str) -> str.trim()));
        //转换大小写
        System.out.println(strHandler("gongj", (str) -> str.toUpperCase()));
    }

    private String strHandler(String str, Function<String,String> fun){
        // 执行 Lamdba 体
        return fun.apply(str);
    }

Predicate 断言形接口 boolean test(T t);

@Test
    public void test4(){
        List<String> list = Arrays.asList("gongj", "yuanj", "xdd");
        System.out.println( filterStr(list,(str) -> str.length() > 3));
    }
    //判断长度>3的字符串
    public List<String> filterStr(List<String> strs, Predicate<String> pre){
        ArrayList<String> arrayList = new ArrayList<>();
        for (String str : strs) {
            // 执行 Lamdba 体
            if(pre.test(str)){
                arrayList.add(str);
            }
        }
        return arrayList;
    }

其他类型的函数式接口

image.png

  • 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。
相关文章
|
2天前
|
分布式计算 Java API
Java 8新特性之Lambda表达式与Stream API
【5月更文挑战第1天】本文将介绍Java 8中的两个重要特性:Lambda表达式和Stream API。Lambda表达式是一种新的函数式编程语法,可以简化代码并提高可读性。Stream API是一种用于处理集合的新工具,可以方便地进行数据操作和转换。通过结合Lambda表达式和Stream API,我们可以更加简洁高效地编写Java代码。
|
4天前
|
Java
java lambda 表达式中的双冒号和箭头的用法
java lambda 表达式中的双冒号和箭头的用法
|
4天前
|
Java API
Java 8新特性之Lambda表达式详解
【4月更文挑战第29天】本文将深入探讨Java 8中引入的Lambda表达式,这是Java历史上的一次重大变革。我们将详细解析Lambda表达式的基本概念,语法结构,以及如何在实际编程中应用。此外,我们还将探讨Lambda表达式与函数式接口的关系,以及它如何改变我们编写和理解Java代码的方式。
|
6天前
|
并行计算 Java API
【专栏】Java中的Lambda表达式应用与实例解析
【4月更文挑战第27天】Java 8的Lambda表达式增强了函数式编程,允许以匿名函数形式传递行为。其基本语法`(params) -&gt; expression/statements`,类型由参数推断,可引用final或effectively final的外部变量。Lambda广泛应用于集合操作(如Stream API)、并行流处理和GUI事件处理,简化代码并提高效率。通过实例,展示了Lambda在集合筛选、并行计算和事件处理中的应用,从而提高开发质量和效率。
|
8天前
|
Java 编译器 API
Java基础教程(17)-Java8中的lambda表达式和Stream、Optional
【4月更文挑战第17天】Lambda表达式是Java 8引入的函数式编程特性,允许函数作为参数或返回值。它有简洁的语法:`(parameters) -> expression 或 (parameters) ->{ statements; }`。FunctionalInterface注解用于标记单方法接口,可以用Lambda替换。
|
8天前
|
Java 测试技术 程序员
滚雪球学Java(21):Java正则表达式
【4月更文挑战第10天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
15 2
滚雪球学Java(21):Java正则表达式
|
9天前
|
人工智能 Java
Java之Lambda表达式的介绍
Java之Lambda表达式的介绍
|
9天前
|
Java
【Java高级】彻底弄懂lambda表达式(上)
【Java高级】彻底弄懂lambda表达式(上)
8 0
|
2天前
|
存储 缓存 前端开发
Java串口通信技术探究3:RXTX库线程 优化系统性能的SerialPortEventListener类
Java串口通信技术探究3:RXTX库线程 优化系统性能的SerialPortEventListener类
11 3