zoukankan      html  css  js  c++  java
  • java8之lambda表达式&方法引用(一)

    本文将简单的介绍一下Lambda表达式和方法引用,这也是Java8的重要更新,Lambda表达式和方法引用最主要的功能是为流(专门负责迭代数据的集合)服务.

    什么是lambda表达式

    可以把lambda表达式理解为简洁的匿名函数.

    我们先声明一个函数式接口(函数式接口:就是只有一个抽象方法的接口. lambda表达式和方法引用,只能用在函数式接口上),比较一下lambda表达式和匿名函数

     

    public interface Animal {
        void cry();
    
        public static void main(String [] args){
                    Animal dog = new Animal() {
                        @Override
                        public void cry() {
                            System.out.println("狗: 汪汪叫");
                        }
                    };
    
                    dog.cry();
    
                    Animal cat  = () -> System.out.println("猫: 喵喵叫");
                    cat.cry();
        }
    }

     

    一个Animal的接口,里面只有一个cry()的抽象方法, 分别用匿名函数和lambda表达式去实现这个接口. 使用lambda表达式的方法非常的简洁,只需要一行.

    lambda表达式语法: 参数 -> 具体的实现.

     

    函数式接口的方法叫做函数描述符,lambda表达式的参数和实现必须和函数描述符的参数和返回值一一对应.cry()方法的参数和返回值都没有所以lambda表达式就是 () -> System.out.println("猫: 喵喵叫");

     

    如果实现有多条语句的话,要写在{}中,并且以;结尾.

    () -> {

        xxx;

        yyy;

        return "ccc";

      }

      

      列举一个高端一点的使用lambda表达式的方法.以Oracle的Emp(员工表)为例.

      表结构

    public class Emp {
        private BigDecimal empno;
    
        private String ename;
    
        private String job;
    
        private BigDecimal mgr;
    
        private Date hiredate;
    
        private Double sal;
    
        private BigDecimal comm;
    
        private BigDecimal deptno;
    
        public BigDecimal getEmpno() {
            return empno;
        }
    
        public void setEmpno(BigDecimal empno) {
            this.empno = empno;
        }
    
        public String getEname() {
            return ename;
        }
    
        public void setEname(String ename) {
            this.ename = ename == null ? null : ename.trim();
        }
    
        public String getJob() {
            return job;
        }
    
        public void setJob(String job) {
            this.job = job == null ? null : job.trim();
        }
    
        public BigDecimal getMgr() {
            return mgr;
        }
    
        public void setMgr(BigDecimal mgr) {
            this.mgr = mgr;
        }
    
        public Date getHiredate() {
            return hiredate;
        }
    
        public void setHiredate(Date hiredate) {
            this.hiredate = hiredate;
        }
    
        public Double getSal() {
            return sal;
        }
    
        public void setSal(Double sal) {
            this.sal = sal;
        }
    
        public BigDecimal getComm() {
            return comm;
        }
    
        public void setComm(BigDecimal comm) {
            this.comm = comm;
        }
    
        public BigDecimal getDeptno() {
            return deptno;
        }
    
        public void setDeptno(BigDecimal deptno) {
            this.deptno = deptno;
        }
    }

    现在我们要写一个方法,过滤所有工资在3000以上的员工(可能有的人可能会想,我直接写sql不得了,费这么多劲干什么,所以我们以下的测试都假设数据是从redis查询出来的.需要手动写过滤条件)

    public List<Emp> filter(List<Emp> listEmp){
            List<Emp> filterList = new ArrayList<>();
            for (Emp emp :listEmp) {
                if (emp.getSal()>3000){
                    filterList.add(emp);
                }
            }
            return filterList;
        }

    这么写的坏处是条件硬编码,如果光是改工资,我们可以把3000抽取为一个参数,但是如果要将条件改为小于呢,如果过滤的是员工的工作呢.可能新手就会进行复制粘贴改一改条件,但是当重复的代码达到一定的数量时,维护起来就是个灾难.


    我们看看Java8提供的函数式编程,可以怎么解决这个方法.(当然使用匿名函数也可以,但是不够简洁).

    把变化的的条件抽取出去,变为一个参数Predicate.具体的实现就是实现这个接口的test方法.

    public List<Emp> filter1(List<Emp> listEmp, Predicate<Emp> predicate){
            List<Emp> filterList = new ArrayList<>();
            for (Emp emp :listEmp) {
                if (predicate.test(emp)){
                    filterList.add(emp);
                }
            }
            return filterList;
        }

    我们利用了java.util.function这个包提供的Predicate接口.这就是一个标准的函数式接口


    测试一下我们写的过滤方法,分别按照工资和工作名称进行过滤
    1 List<Emp> filterSalEmp = empService.filter1(listEmp, Emp emp -> emp.getSal() > 3000);
    2 List<Emp> filterJobEmp = empService.filter1(listEmp, Emp emp -> "SALMAN".equals(emp.getJob()));

     Predicate接口的方法 boolean test(T t); 返回值是Boolean类型的,参数是任意类型   我们的实现 Emp emp -> emp.getSal() > 3000 参数Emp ,返回Boolean类型的值 emp.getSal() > 3000  完全满足. 可以看到使用函数式接口编程提高了代码的灵活性和可重用性.

    其实lambda表达式的类型是可以从上下文中自己推断出来的,也就是说 上面的 lambda的参数  Emp emp  可以不带参数类型.写成下面这样

    1 List<Emp> filterSalEmp = empService.filter1(listEmp, emp -> emp.getSal() > 3000);
    2 List<Emp> filterJobEmp = empService.filter1(listEmp, emp -> "SALMAN".equals(emp.getJob()));

    lambda表达式使用局部变量

    回到之前的例子:

    String catCry = "猫: 喵喵叫";
    Animal cat = () -> System.out.println(catCry);
    cat.cry();
    打印输出:
    猫: 喵喵叫

    lambda表达式可以使用局部变量,但是必须是final类型的或事实上final类型的(不可改变).
    <<java8实战>>中的解释:
    
    
    
    

    第一,实例变量和局部变量背后的实现有一
    个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局
    部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线
    程将这个变量收回之后,去访问该变量。因此,Java在访问自由局部变量时,实际上是在访问它
    的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了
    这个限制。
    第二,这一限制不鼓励你使用改变外部变量的典型命令式编程模式(我们会在以后的各章中
    解释,这种模式会阻碍很容易做到的并行处理)。

     方法引用

    方法引用可以理解为lambda表达式的快捷写法,它比lambda表达式更加的简洁,可读性更高.有更好的重用性.如果实现比较简单,一句话就可以实现,复用的地方又不多推荐使用lambda表达式,否则应该使用方法引用.  

    方法引用的格式  类名::方法名

    我们使用方法引用的方式,重新实现上面刚刚过滤员工表的例子.

    定义两个条件类,方法的参数和返回值定义的和predicate的函数名描述符一致

    public class EmpConditionA {
    
        public static boolean test(Emp emp) {
            return emp.getSal() > 3000;
        }
    }

    public class EmpConditionB{

    public static boolean test(Emp emp) {
    return "engineer".equals(emp.getJob());
    }
    }
     

    实现方式: 使用类名::方法的方式

    List<Emp> listEmp = empService.listEmp();
    List<Emp> filterSalEmp = empService.filter1(listEmp, EmpConditionA::test);
    List<Emp> filterJobEmp = empService.filter1(listEmp, EmpConditionB::test);

    因为这个方法调用的是第三方类的方法所以是static

    还有两种调用方式: 一种是直接调用流中的实例的方式,还有一种是调用局部变量的方式.

    直接调用流中的实例的方式: 注意下面的Emp::getJob 就相当于集合中每一个emp对象都调用自己的getJob方法.

    这个例子是讲将集合转换为流,map()方法可以理解为对集合的每一个元素进行相应的操作,这里就是对每一个emp实例调用getJob方法.最后.collect(Collectors.toList())将流转换为新的list集合(关于流,笔者后面会继续更新相关的博客).

    listEmp.stream().map(Emp::getJob).collect(Collectors.toList());

    调用局部变量的方式: 创建条件EmpconditionA的实例

    EmpConditionA empConditionA = new EmpConditionA();
    List<Emp> filterSalEmp = empService.filter1(listEmp, empConditionA::test);

     好了关于lambda表达式和方法引用就简单的介绍到这里,

    限于篇幅有些地方介绍的不是很详细,如果有疑问欢迎大家随时提问.

  • 相关阅读:
    ECharts之柱状图 饼状图 折线图
    Vue自定义指令(directive)
    HDU 1231 最大连续子序列
    POJ 2533 Longest Ordered Subsequence
    HDU 1163 Eddy's digital Roots
    HDU 2317 Nasty Hacks
    HDU 2571 命运
    HDU 4224 Enumeration?
    HDU 1257 最少拦截系统
    HDU 2740 Root of the Problem
  • 原文地址:https://www.cnblogs.com/xisuo/p/9705944.html
Copyright © 2011-2022 走看看