lambda表达式&函数式接口&方法引用 构造器引用

Lambda表达式

什么是Lambda表达式

Lambda 是一个匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

举个栗子

    //从匿名内部类到Lambda的转化
    public void test1() {
        //匿名内部类实现线程接口
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!");
            }
        };
        //Lambda表达式实现线程接口
        Runnable r2 = () -> System.out.println("Hello World!");
    }
善变的需求

在工作中我们可能经常遇到对一堆固定数据这样儿那样儿的需求。比如:

    public List<Student> stus = Arrays.asList(
            new Student("小康", 16, 98.5),
            new Student("小陈", 28, 60),
            new Student("小伟", 20, 42),
            new Student("小孙", 8, 85)
    );
  • 筛选出60分以上的学生;
  • 筛选出18岁以下的学生;
  • 筛选出姓陈的学生;
  • 对学生进行分数正向排序

策略模式

将筛选条件抽象成方法复用 - - - ? 产生大量冗余代码,难以维护。
传统设计模式中策略模式可以解决此类问题。

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

通过行为族的规定去替换规则策略。

在这里插入图片描述

匿名内部类减少策略类

直接通过匿名内部类的形式实现规则接口,减少大量的策略类。

    //匿名内部类
    @Test
    public void test3() {
        //获取18岁以下的学生
        List<Student> ageStus = filterStudent(stus, new FilterRule<Student>() {
            @Override
            public boolean myRule(Student student) {
                return student.getAge() < 18;
            }
        });
        //获取60分以上的学生
        List<Student> scoreStus = filterStudent(stus, new FilterRule<Student>() {
            @Override
            public boolean myRule(Student student) {
                return student.getAge() >= 60;
            }
        });
    }
通过Lambda表达式减少匿名内部类代码

通过Lambda表达式可以有效减少大量的匿名内部类代码。

    //Lambda表达式
    @Test
    public void test4() {
        //获取18岁以下的学生
        List<Student> ageStus = filterStudent(stus, (s) -> s.getAge() <= 18);
        //获取60分以上的学生
        List<Student> scoreStus = filterStudent(stus, (s) -> s.getScore() >= 60);
    }
Lambda语法

我们可以看出Lambda主要的目的还是改造匿名内部类对接口的实现上。我们可以把整个Lambda体的结果当做为一个接口的匿名内部类实现后返回的结果

箭头操作符:

Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “ ->” , 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:
左侧: 指定了 Lambda 表达式需要的所有参数。
右侧: 指定了 Lambda 体,即 Lambda 表达式要执行的功能。

基本语法:
  • 语法格式一: 无参,无返回值, Lambda 体只需一条语句
Runnable runnable = () -> System.out.println("Hello World");
  • 语法格式二: 需要一个参数(此时小括号可以省略
Consumer<String> consumer1 = (str) -> System.out.println(str);
Consumer<String> consumer2 = str -> System.out.println(str);
  • 语法格式三: 需要两个参数,并且有返回值(多条语句必须有大括号)
BinaryOperator<Integer> bo = (Integer x, Integer y) -> {
            System.out.println(x + "+" + y + "=");
            return x + y;
        };
  • 语法格式四:当 Lambda 体只有一条语句时, return 与大括号可以省略
BinaryOperator<Integer> bo2 = (Integer x, Integer y) -> x + y;

Runnable 、Consumer、BinaryOperator、BinaryOperator这些接收返回值的都是接口类型,后边Lambda体相当于一个匿名内部类对接口的实现。
思考:语法中所提的参数、返回值是哪的?接口中哪个方法的?


函数式接口

什么是函数式接口:

只包含一个抽象方法的接口,称为函数式接口。

使用:
  • 可以通过 Lambda 表达式来创建该接口的对象。
  • 接口上使用 @FunctionalInterface注解,可以检查它是否是函数式接口。
  • 若不是函数式接口,创建Lambda表达式时报错。
    在这里插入图片描述
声明一个函数式接口:

在这里插入图片描述

作为参数传递Lambda表达式:

在这里插入图片描述

  • 为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型
Java内置函数式接口:
接口函数描述符方法名
Predicate断言型接口T->boolean确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法boolean test(T t)
Consumer消费型接口T->void对类型为T的对象应用操作,包含方法:void accept(T t)
Function<T,R>函数型接口T->R对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法: R apply(T t);
Supplier供给型接口()->T返回类型为T的对象,包含方法:T get();
函数式接口使用实例:
  • 消费型接口: 参数有来无回
    public void shopping(Double money, Consumer<Double> con) {
        con.accept(money);
    }
    
    //Consumer<T> 消费型接口 :
    @Test
    public void test1() {
        shopping(666.8, (m) -> System.out.println("逛淘宝消费了:" + m + "元"));
    }
  • 供给型接口: 无传参 但返回资源
    //产生指定个数的随机数返回
    public List<Integer> getNumList(int count, Supplier<Integer> sup) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            Integer n = sup.get();
            list.add(n);
        }
        return list;
    }

    //Supplier<T> 供给型接口 :
    @Test
    public void test2() {
        List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
    }
  • 函数型接口: 参数和返回值类型可不同可相同
    //获取字符串长度
    public Integer getLength(String str, Function<String,Integer> fun) {
        return fun.apply(str);
    }

    //Function<T, R> 函数型接口:
    @Test
    public void test3() {
        Integer chenLen = getLength("ChenYouXiu", (str) -> str.length());
    }
  • 断言型接口: 返回值为布尔
    //需求:将满足条件的字符串,放入集合中
    public List<String> filterStr(List<String> list, Predicate<String> pre){
        List<String> strList = new ArrayList<>();
        for (String str : list) {
            if(pre.test(str)){
                strList.add(str);
            }
        }
        return strList;
    }

    //Predicate<T> 断言型接口:
    @Test
    public void test4(){
        List<String> strList = filterStr(Arrays.asList("Hello", "JK", "Lambda", "www", "ok"),
                (s) -> s.length() > 3);
    }
  • 还有许多JDK提供的函数式子接口 可以多多探索

方法引用、构造器引用

这两种引用我理解为Lambda语法中的一种特殊语法。

方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。
原则: 函数式接口中方法的参数列表,必须与方法引用方法的参数列表保持一致!
语法:
使用操作符两个冒号 “::” 将方法名和对象或类的名字分隔开来。三种主要使用情况:

  • 对象::实例方法
  • 类::静态方法
  • 类::实例方法

实例:

        Consumer<String> con1 = (x)-> System.out.println(x);
        Consumer<String> con2 = System.out::println;
        
        con1.accept("Hello Java8!");
        con2.accept("Hello Java8!");

只有参数数量一致,才可以使用方法引用。
在这里插入图片描述

构造器引用

格式: ClassName::new
说明: 与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!
在这里插入图片描述


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值