Lambda详情说明及教程

85 阅读7分钟

Lambda表达式

Lambda表达式使用的前提

Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用,使用时有几个条件要特别注意

1.方法的参数或局部变量类型必须为接口才能使用Lambda

2.接口中有且仅有一个抽象方法(@Functionallnterface)

使用Lambda表达式主要核心是使用在Stream流中。

使用lambda表达式的前提是该接口必须为函数式接口(有且仅有一个抽象方法)

@FunctionalInterface :该注解可以判断接口是否为函数式接口

Lambda表达式无参无返回值语法

函数式接口 变量名 =()->{方法体}

这里使用runnable举例

//匿名内部类
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("使用匿名内部类的方式执行线程任务");
    }
};
//转化为lambda表达式
Runnable run =()->{
    System.out.println("这是使用lambda表达式的线程任务");
};

自定义函数式接口使用Lambda表达式

public class Test02 {

    public static void main(String[] args) {
        
        fun(()-> {
                System.out.println("我在lambda中游泳");
        });
    }

    public static void fun(Swimming w){
            w.crawl();
    }
}
//函数式接口
@FunctionalInterface
interface Swimming{
    public void crawl();
}

有参有返回值

函数式接口 变量名 =(参数1,参数2)->{方法体}

package com.cjj.demo03;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Test03 {

    public static void main(String[] args) {
        ArrayList<Student> student = new ArrayList<>();

        student.add(new Student("成俊杰",18));
        student.add(new Student("阿光",25));
        student.add(new Student("陈重豪",19));
        student.add(new Student("王俊齐",28));
        System.out.println(student);
/*        //对集合中的元素按照年龄排序,从大到小排序
        //collections:集合工具类
        Comparator<Student> comparator = new Comparator<Student>() {
            @Override
            //如果是0表示相同,大于0表示o1大于o2
            public int compare(Student o1, Student o2) {
                return o1.getAge()-o2.getAge();
            }
        };
        //collections中的排序方法,第一个参数为需要排序的集合,第二个排序的规则
        Collections.sort(student,comparator);
        System.out.println(student);
        */

        //使用Lambda表达式的方式进行排序,有参有返回值
        Comparator<Student> comparator2 = (o1,o2) ->{ //Lambda表达式就是对函数式接口中抽象方法的简写
            //如果是0表示相同,大于0表示o1大于o2
                return o1.getAge()-o2.getAge();
        };
        Collections.sort(student,comparator2);
        System.out.println(student);
    }

}

class Student{

    String name;
    int age;


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

Lambda表达式的简写规则

1.小括号内的参数类型可以省略

2.如果小括号内有且仅有一个参数,则小括号可以省略

3.如果大括号内有且仅有一个语句,可以同时省略大括号,return关键字及语句分号

package com.cjj.demo02;

public class Test02 {

    public static void main(String[] args) {

        //有且仅有一个参数,省略小括号
        Swimming swimming = a1-> System.out.println("输出省略小括号的语句"+a1);
        fun(swimming);
        //有且仅有一个语句,可以同时省略大括号,return关键字以及语句分号
        Swimming2 swimming2 = a2-> a2*2;
        fun2(swimming2);
    }

    public static void fun(Swimming w){
            w.crawl(15);
    }


    public static void fun2(Swimming2 w2){
        int i = w2.crawl2(12);
        System.out.println(i);
    }

}
//函数式接口
@FunctionalInterface
interface Swimming{
    public void crawl(int a1);
}


@FunctionalInterface
interface Swimming2{
    public int crawl2(int a2);
}

内置函数式接口

想要使用Lambda表达式,他的前提就是必须是函数式接口

内置函数式接口的由来

package com.cjj.demo04;

public class Test04 {

    public static void main(String[] args) {
        Operation operation = arr-> {
            int sum=0;
            for (int s:arr){
                sum+=s;
            }
            return sum;
        };
        fun(operation);
    }

    public static void fun(Operation operation){
        int[] arr ={1,2,3,4};
        int s =operation.getSum(arr);
        System.out.println("数组的和"+s);
    }

}
interface Operation{
    public int getSum(int[] arr);
}

分析:

我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方便,JDK内置提供了大量常用的函数式接口,大多数无需自己再定义函数式接口,而可以直接使用jdk内置的函数式接口,大约分成了四类。

image.png

消费型函数式接口Consumer

适合有参无返回值

示例:

package com.cjj.demo05;

import java.util.function.Consumer;

public class Test05 {
    public static void main(String[] args) {

        Consumer<Double> doubleConsumer = t-> {
            System.out.println("今天吃饭花费"+t+"元");
            };
        fun(doubleConsumer,500.0);
        }
        //有参无返回值,直接使用消费型函数式接口Consumer
    public static void fun(Consumer<Double> consumer , Double money){
        consumer.accept(money);
    }
}
供给型函数式接口Supplier

适合无参有返回值

示例:

package com.cjj.demo06;

import java.util.function.Consumer;
import java.util.function.Supplier;

public class Test06 {
    public static void main(String[] args) {

        Supplier<Double> doubleSupplier = ()-> 1000.0;
        fun(doubleSupplier);
    }
    //无参有返回值,直接使用供给型接口Supplier内置函数式接口
    public static void fun(Supplier<Double> supplier){
        Double aDouble = supplier.get();
        System.out.println(aDouble);
    }
}
函数型函数式接口--Function<T,R>

适合有参有返回值

T代表参数的类型

R代表返回值的类型

package com.cjj.demo07;

import java.util.function.Function;
import java.util.function.Supplier;

public class Test07 {
    public static void main(String[] args) {
        Function<Double,String> function=d-> "今天吃饭花了"+d+"元";
        fun(function,25.2);
    }
    //有参有返回值,直接使用函数型接口Function
    public static void fun(Function<Double,String> function,Double d){
        String apply = function.apply(d);
        System.out.println(apply);
    }
}
断言型函数式接口Predicate

判断是否满足条件

T为参数

boolean为返回值类型

package com.cjj.demo08;

import java.util.function.Function;
import java.util.function.Predicate;

public class Test08 {
    public static void main(String[] args) {
        Predicate<Integer> predicate =t->t>=19;
        fun(predicate,18);
    }
    //判断是否满足条件,直接使用函数型接口Predicate
    public static void fun(Predicate<Integer> predicate, Integer d){
        boolean test = predicate.test(d);
        System.out.println("是否成年"+test);
    }
}

方法引用

特殊的Lambda表达式,它是对Lambda表达式的一种简写方式。

类名::方法名

方法引用的由来

package com.cjj.demo09;

import java.util.function.Consumer;

public class Test09 {

    public static void main(String[] args) {
        Consumer<int[]> consumer=t->{
            int sum=0;
            for (int i : t) {
                sum+=i;
            }
            System.out.println("数组的和"+sum);
        };
        fun(consumer);
    }

    public static void fun(Consumer<int[]> consumer){
        int[] arr = {1,2,3,4,5,6};
        consumer.accept(arr);
    }

    public static void sum(int[] arr){
        int sum =0;
        for (int i : arr) {
            sum+=i;
        }
        System.out.println("数组的和是"+sum);
    }
}

分析:

如果我们在Lambda表达式中所指定的功能,已经有其他方法存在相同方案,那是否还有必要再写重复的逻辑?可以直接"引用过去就好了":---方法引用

上面案例经过方法引用改变后的代码如下:

package com.cjj.demo09;

import java.util.function.Consumer;

public class Test09 {

    public static void main(String[] args) {
        Consumer<int[]> consumer=Test09::sum;
        fun(consumer);
    }

    public static void fun(Consumer<int[]> consumer){
        int[] arr = {1,2,3,4,5,6};
        consumer.accept(arr);
    }

    public static void sum(int[] arr){
        int sum =0;
        for (int i : arr) {
            sum+=i;
        }
        System.out.println("数组的和是"+sum);
    }
}
package com.cjj.demo09;

import java.util.function.Consumer;

public class Test09 {

    public static void main(String[] args) {
        fun(Test09::sum);
    }

    public static void fun(Consumer<int[]> consumer){
        int[] arr = {1,2,3,4,5,6};
        consumer.accept(arr);
    }

    public static void sum(int[] arr){
        int sum =0;
        for (int i : arr) {
            sum+=i;
        }
        System.out.println("数组的和是"+sum);
    }
}

方法引用的类型

方法引用是Lambda表达式中的一种简写。如果lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。

image.png

image.png

静态方法引用

(args)->类型.静态方法(args). 当Lambda表达式中的方法体,只有一条语句,而这条语句是类型.静态方法。并且静态方法的参数和lambda的参数一致时。

类名::静态方法;

实例方法引用

(args)->inst.instMethod(args)

实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用 类无需实例化,直接用类名去调用

类名::方法名

public class Test10 {
    public static void main(String[] args) {
        //创建一个实例对象
        Student s = new Student("cjj",21);
        //lambda表达式
        Supplier<String> supplier = ()->s.getName();
        String s2 = supplier.get();
        System.out.println(s2);
        //方法引用
        Supplier<String> sup=s::getName;
        //拿到返回值
        String s1 = sup.get();
        System.out.println(s1);
    }

}
class Student{
    String name;
    Integer age;

    public Student() {
    }

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public Integer getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(Integer age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

对象方法引用

lambda: (inst,args)->inst.普通方法(args)----->类名::普通方法

若lambda参数类列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。

public class Test11 {
    public static void main(String[] args) {
        //使用lambda表达式判断两个字符串是否一致
        BiFunction<String,String,Boolean> biFun = (s1,s2)->s1.equals(s2);
        Boolean apply = biFun.apply("cjj", "cjj");
        System.out.println(apply);
        //转换为对象方法引用的方式判断
        BiFunction<String,String,Boolean> biFun2 = String::equals;
        Boolean apply2 = biFun.apply("cjj", "cjj");
        System.out.println(apply2);
    }
}

构造方法引用

(args)->new 类名(args)

类名::new

public class Test12 {

    public static void main(String[] args) {
        //lambda 无参有返回值
        Supplier<Student> supplier = ()->new Student();
        final Student student = supplier.get();
        System.out.println(student);
        //转换为构造方法引用
        Supplier<Student> supplier2 = Student::new;
        final Student student2 = supplier2.get();
        System.out.println(student2);
        //lambda有参有返回值
        BiFunction<String,Integer,Student> biFunction=(n,a)->new Student(n,a);
        Student cjj = biFunction.apply("cjj", 22);
        System.out.println(cjj);
        //转换为构造方法引用
        BiFunction<String,Integer,Student> biFunction2=Student::new;
        Student cjj2 = biFunction.apply("cjj", 23);
        System.out.println(cjj2);
    }
}