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
Java 正则表达式高级用法
Java 中的正则表达式是强大的文本处理工具,用于搜索、匹配、替换和分割字符串。`java.util.regex` 包提供了 `Pattern` 和 `Matcher` 类来高效处理正则表达式。本文介绍了高级用法,包括使用 `Pattern` 和 `Matcher` 进行匹配、断言(如正向和负向前瞻/后顾)、捕获组与命名组、替换操作、分割字符串、修饰符(如忽略大小写和多行模式)及 Unicode 支持。通过这些功能,可以高效地处理复杂文本数据。
|
3天前
|
Java 程序员 API
Java中的Lambda表达式:简化代码的秘密武器
在Java 8中引入的Lambda表达式是一种强大的编程工具,它可以显著简化代码,提高可读性。本文将介绍Lambda表达式的基本概念、优势以及在实际开发中的应用。通过具体示例,您将了解如何使用Lambda表达式来简化集合操作、线程编程和函数式编程。让我们一起探索这一革命性的特性,看看它是如何改变Java编程方式的。
16 4
|
3天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码
【8月更文挑战第49天】在Java 8的发布中,Lambda表达式无疑是最令人兴奋的新特性之一。它不仅为Java开发者提供了一种更加简洁、灵活的编程方式,而且还极大地提高了代码的可读性和开发效率。本文将通过实际代码示例,展示如何利用Lambda表达式优化和重构Java代码,让你的编程之旅更加轻松愉快。
|
6天前
|
Java 开发者
探索Java中的Lambda表达式:简化代码,提升效率
【9月更文挑战第14天】本文旨在揭示Java 8中引入的Lambda表达式如何革新了我们编写和管理代码的方式。通过简洁明了的语言和直观的代码示例,我们将一起走进Lambda表达式的世界,了解其基本概念、语法结构以及在实际编程中的应用。文章不仅会展示Lambda表达式的魅力所在,还会指导读者如何在日常工作中有效利用这一特性,以提高编码效率和程序可读性。
|
14天前
|
存储 Java
探索Java中的Lambda表达式
【9月更文挑战第6天】Lambda表达式是Java 8引入的一个强大特性,它允许我们将函数作为参数传递或作为返回值。在这篇文章中,我们将深入探讨Lambda表达式的概念、语法和用法,以及如何在实际项目中应用它们来简化代码。通过学习本文,你将能够更好地理解Lambda表达式的作用,并掌握如何在Java中使用它们。
|
12天前
|
并行计算 Java 开发者
探索Java中的Lambda表达式:简化代码,提升效率
Lambda表达式在Java 8中引入,旨在简化集合操作和并行计算。本文将通过浅显易懂的语言,带你了解Lambda表达式的基本概念、语法结构,并通过实例展示如何在Java项目中应用Lambda表达式来优化代码,提高开发效率。我们将一起探讨这一现代编程工具如何改变我们的Java编码方式,并思考它对程序设计哲学的影响。
|
18天前
|
安全 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法的深度融合
在Java的演进历程中,Lambda表达式无疑是Java 8引入的一项革命性特性,它极大地简化了函数式编程在Java中的应用,使得代码更加简洁、易于阅读和维护。而这一切的背后,JVM的invokedynamic指令功不可没。本文将深入探讨invokedynamic指令的工作原理及其与Java Lambda语法的紧密联系,带您领略这一技术背后的奥秘。
13 1
|
20天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码
【8月更文挑战第31天】 在Java 8的发布中,Lambda表达式无疑是最令人兴奋的新特性之一。它不仅为Java开发者提供了一种更加简洁、灵活的编程方式,而且还极大地提高了代码的可读性和开发效率。本文将通过实际代码示例,展示如何利用Lambda表达式优化和重构Java代码,让你的编程之旅更加轻松愉快。
|
22天前
|
Java API
Java 8新特性:Lambda表达式与Stream API的深度解析
【7月更文挑战第61天】本文将深入探讨Java 8中的两个重要特性:Lambda表达式和Stream API。我们将首先介绍Lambda表达式的基本概念和语法,然后详细解析Stream API的使用和优势。最后,我们将通过实例代码演示如何结合使用Lambda表达式和Stream API,以提高Java编程的效率和可读性。
|
1月前
|
存储 算法 Oracle
19 Java8概述(Java8概述+lambda表达式+函数式接口+方法引用+Stream+新时间API)
19 Java8概述(Java8概述+lambda表达式+函数式接口+方法引用+Stream+新时间API)
55 8