Java8新特性:Lambda表达式

这里写目录标题

    • 什么是Lambda表达式
    • Lambda表达式的优缺点
      • 优点:
      • 缺点:
    • 体验一下Lambda表达式的便捷
      • 无优化
      • 优化方式一:引入策略设计模式
      • 优化方式二:匿名内部类
      • 优化方式三:lambda表达式
      • 优化方式四:Stream API
    • Lambda表达式的基本语法
      • 内置的四大核心函数式接口
    • Lambda表达式中的引用
      • 方法引用
        • 对象::实例方法名
        • 类::静态方法名
        • 类::实例方法名
      • 另外,还有构造器引用和数组引用
    • 总结

什么是Lambda表达式

百度说 “Lambda 表达式”是一个复匿名函数,它可以包含表达式和语句,制并且可用于创建委托或表达式树类型。

Lambda表达式的优缺点

优点:

  1. 代码简洁,开发迅速
  2. 方便函数式编程
  3. 非常容易进行并行计算
  4. java引入lambda,改善了集合操作(引入Stream API)

缺点:

  1. 代码可读性变差
  2. 使得语言学习曲线陡峭,学习难度提升
  3. 性能方面,在非并行计算中,很多计算未必有传统的for性能要高
  4. 不容易进行调试

体验一下Lambda表达式的便捷

无优化

需求:获取当前公司中员工年龄大于35岁的员工信息

	List<Employee> employees = Arrays.asList(
			//id、name、age、salary
            new Employee(1, "张三", 35, 9999.9),
            new Employee(2, "李四", 18, 18888.0),
            new Employee(3, "王五", 44, 14444.0),
            new Employee(4, "赵六", 23, 2333.0),
            new Employee(5, "钱七", 47, 14244.0),
            new Employee(6, "陈八", 34, 7444.0),
            new Employee(7, "曹九", 39, 944.0)
    );

一般的做法是,直接写个过滤方法 如下

	@Test
    public void test3() {
        List<Employee> filter = filterEmployee(employees);
        for (Employee employee : filter) {
            System.out.println(employee);
        }
    }
	public List<Employee> filterEmployee(List<Employee> list) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
            if (emp.getAge() > 35) {
                emps.add(emp);
            }
        }
        return emps;
    }

但是如果又增加了需求,又要重新写一个方法,然后直接cope上面的只改一句话,这样代码冗长,繁琐
需求:获取当前公司中员工工资大于5000的员工信息

	public List<Employee> filterEmployee2(List<Employee> list) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
            //只需改变一句话,其他完全一样,代码冗长
            if (emp.getSalary() > 5000.0) {
                emps.add(emp);
            }
        }
        return emps;
    }

优化方式一:引入策略设计模式

新建一个接口,和一个实现该接口的类

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

--------------分割线----------------

public class FilterEmpByAge implements MyPredicate<Employee> {
    @Override
    public boolean test(Employee employee) {
        return employee.getAge()>35;
    }
}

然后新建方法,如果mp.test(emp)为true,则执行emp添加到新数组,完成过滤

	@Test
    public void test4(){
        MyPredicate<Employee> mp = new FilterEmpByAge();
        List<Employee> employees = filterEmployee(this.employees, mp);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }

	public List<Employee> filterEmployee(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 test6(){
        MyPredicate<Employee> mp = new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getSalary()>=9797;
            }
        };
        List<Employee> employees = filterEmployee(this.employees, mp);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }

实现匿名内部类多多少少还是有点繁琐,毕竟有效代码就一句话
return employee.getSalary()>=9797;

优化方式三:lambda表达式

使用lambda表达式如下,做到了正在的简洁,只需要把最关键的东西写出来就行了

	@Test
    public void test7(){
        List<Employee> list = filterEmployee(employees, (e) -> e.getSalary() <= 5000);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }

在这之后还有一种方式,那就是Stream API

优化方式四:Stream API

这个极简,连filterEmployee方法都不用写就完成了过滤

	@Test
    public void test8(){
        employees.stream()
                .filter(e -> e.getSalary() >= 5000) //过滤
                .limit(1) //限制1个
                .forEach(System.out::println); //遍历
    }

Lambda表达式的基本语法

体验到了Lambda表达式的便捷后,就来学习它的基本语法吧~

一、Lambda 表达式的基础语法:
          "->" 箭头操作符 或 Lambda 操作符
              左侧:Lambda 表达式的参数列表
              右侧:Lambda 表达式所需执行的功能,即Lambda体
    
          语法格式一:无参数,无返回值
              () -> System.out.println("Hello World");
    
          语法格式二:有一个参数,并且无返回值
              (e) -> System.out.println(e);
              小括号可以不写
              e -> System.out.println(e);
    
          语法格式三:有两个以上的参数,有返回值,并且Lambda 体中有多条语句
              如果只有一条语句,return和{}都可以省略不写(通用)
    
          语法格式四:Lambda 表达式的参数类型可以省略不写
              因为JVM编译器可以通过上下文推断出参数数据类型,叫做”类型推断“
    
         上联:左右遇一括号省
         下联:左侧推断类型省
         横批:能省就省
二、Lambda 表达式需要”函数式接口“的支持
   函数式接口:接口中只有一个抽象方法的接口,成为函数式接口
      可以在接口上使用注解@FunctionalInterface 修饰,检查接口是否为函数式接口

内置的四大核心函数式接口

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

Supplier :供给型接口
T get();

Function :函数型接口
R apply(T t);

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

Lambda表达式中的引用

方法引用

方法引用主要有三种语法格式:
对象::实例方法名
类::静态方法名
类::实例方法名
注意:Lambda 体中调用方法的参数列表返回值类型,要与函数式接口中抽象方法的函数列表返回值类型保持一致

对象::实例方法名

	/**
     * 例子1
     */
    @Test
    public void test1(){
        //正常的Lambda表达式
        Consumer<String> con = (x) -> System.out.println(x);
        //方法引用
        //println为out对象的一个实例方法
        PrintStream ps = System.out;
        Consumer<String> con2 = ps::println;
        Consumer<String> con3 = System.out::println;
    }
	/**
     * 例子2
     */
    @Test
    public void test2(){
        Employee emp = new Employee(1,"niu",33,49999.0);
        //正常的Lambda表达式
        Supplier<String> sup = () -> emp.getName();
        System.out.println(sup.get());
        //方法引用
        //getSalary为emp对象的一个实例方法
        Supplier<Double> sup2 = emp::getSalary;
        System.out.println(sup2. get());
    }
    

类::静态方法名

	@Test
    public void test3(){
        Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
		//compare是Integer类的静态方法
        Comparator<Integer> com2 = Integer::compare;
    }

类::实例方法名

这个要注意,只有当第一个参数是实例方法的调用者第二个参数是实例方法的参数时 才能这样使用

	/**
     * 类::实例方法名
     *
     * 第一个参数是实例方法的调用者,第二个参数是实例方法的参数时 即可这样使用
     */
    @Test
    public void test4(){
        BiPredicate<String,String> bp = (x,y) -> x.equals(y);
        //x是equals方法调用者,y为equals方法参数
        BiPredicate<String,String> bp2 = String::equals;

    }

另外,还有构造器引用和数组引用

如果接口中方法有参数,则调用的构造器参数和接口方法中的参数一致
如果没有参数,则调用的无参构造器

	/**
     * 构造器引用
     *
     * 如果接口中方法有参数,则调用的构造器参数和接口方法中的参数一致
     * 如果没有参数,则调用的无参构造器
     * 
     * 例子1
     */
    @Test
    public void test5(){
        //接口的方法中无参数,调用的无参构造器
        Supplier<Employee> sup = ()-> new Employee();
        Supplier<Employee> sup1 = Employee::new;
    }
	/**
	 * 例子2
	*/	
    @Test
    public void test6(){
        //接口的方法中有一个参数,调用的构造器也有一个参数
        Function<Integer,Employee> function = Employee::new;
        Employee employee = function.apply(1);
        System.out.println(employee);
        //结果 Employee{id=1, name='null', age=null, salary=null}
    }

    /**
     * 数组引用
     */
    @Test
    public void test7(){
        Function<Integer,String[]> fun = (x) -> new String[x];

        Function<Integer,String[]> fun2 = String[]::new;

        String[] apply = fun2.apply(2);
    }

总结

学习使我快乐

你可能感兴趣的:(java,java)