Lambda表达式

Lambda表达式

一、Lambda表达式简介

什么是Lambda?

Lambda是 JAVA 8添加的新特性,说白了,Lambda是一个匿名函数

同时,它也称为闭包,Lambda表达式允许把函数作为一个方法的参数(函数作为参数传递到方法中

为什么使用Lambda

使用Lambda表达式可以对一个接口的方法进行非常简洁的实现

Lambda对接口的要求

虽然可以使用Lambda表达式对某些接口进行简单的实现,但是并不是所有的接口都可以用Lambda表达式来实现,要求接口中定义的必须要实现的抽象方法只能是一个

在JAVA8中 ,对接口加了一个新特性:default
可以使用default对接口方法进行修饰,被修饰的方法在接口中可以默认实现

@FunctionalInterface

修饰函数式接口的,接口中的抽象方法只有一个

二、Lambda的基础语法

1.语法

// 1.Lambda表达式的基础语法
// Lambda是一个匿名函数 一般关注的是以下两个重点
// 参数列表 方法体

/**
* ():用来描述参数列表
*  {}:用来描述方法体 有时可以省略
*  ->: Lambda运算符 读作goes to
*  例 Test t=()->{System.out.println("hello word")}; 大括号可省略
*/

2.创建多个函数式接口

/**
 * Created by FengBin on 2021/8/12 19:03
 * 无返回值无参数的函数式接口
 */
@FunctionalInterface
public interface NoneReturnNoneParameter {
    void test();
}
/**
 * Created by FengBin on 2021/8/12 19:03
 * 无返回值单个参数的函数式接口
 */
@FunctionalInterface
public interface NoneReturnSingleParameter {
    void test(int num);
}
/**
 * Created by FengBin on 2021/8/12 19:04
 * 无返回值多个参数的函数式接口
 */
@FunctionalInterface
public interface NoneReturnMultipleParameter {
    void test(int num1, int num2);
}
/**
 * Created by FengBin on 2021/8/12 19:11
 * 单个返回值无参数的函数式接口
 */
@FunctionalInterface
public interface SingleReturnNoneParameter {
    int test();
}
/**
 * Created by FengBin on 2021/8/12 19:11
 * 单个返回值单个参数的函数式接口
 */
@FunctionalInterface
public interface SingleReturnSingleParameter {
    int test(int num);
}
/**
 * Created by FengBin on 2021/8/12 19:11
 * 单个返回值多个参数的函数式接口
 */
@FunctionalInterface
public interface SingleReturnMutipleParameter {
    int test(int num1, int num2);
}

3.创建测试类

package com.feng.lambda.lambda_test.test;
import com.feng.lambda.lambda_test.lambda_interface.*;
import org.junit.jupiter.api.Test;

/**
 * Created by FengBin on 2021/8/12 19:06
 */
public class TestLambdaInterface {

    @Test
    public void test01() {
        //测试无返回值无参数的函数式接口
        NoneReturnNoneParameter lambda = () -> {
            System.out.println("无返回值无参数接口的Lambda表达式...");
        };
        lambda.test();
    }
    @Test
    public void test02() {
        //测试无返回值单个参数的函数式接口
        NoneReturnSingleParameter lambda = (int num) -> {
            System.out.println(num);
        };
        lambda.test(10);
    }

    @Test
    public void test03() {
        //测试无返回值多个参数的函数式接口
        NoneReturnMultipleParameter lambda = (int num1, int num2) -> {
            System.out.println(num1 + num2);
        };
        lambda.test(10,20);
    }

    @Test
    public void test04() {
        //测试有返回值无参数的函数式接口
        SingleReturnNoneParameter lambda = () -> {
            System.out.println("有返回值无参数的Lambda表达式...");
            return 10;
        };
        lambda.test();
    }

    @Test
    public void test05() {
        //测试有返回值单个参数的函数式接口
        SingleReturnSingleParameter lambda = (int num) -> {
            return num;
        };
        int res = lambda.test(10);
        System.out.println(res);
    }

    @Test
    public void test06() {
        //测试有返回值多个参数的函数式接口
        SingleReturnMutipleParameter lambda = (int num1,int num2) -> {
            return num1 + num2;
        };
        int res = lambda.test(10,20);
        System.out.println(res);
    }
}

三、语法精简

针对上述基础语法的精简

1.参数类型精简

//1. 对参数类型进行精简:由于在接口中我们已经定义了参数的类型,故我们在使用Lambda表达式的时候,可以省略参数类型,自动为我们匹配类型

NoneReturnMultipleParameter lambda = (int num1, int num2) -> {
    System.out.println(num1 + num2);
};

//对于上面的Lambda表达式我们进行如下的精简
NoneReturnMultipleParameter lambdaSimple = (num1,num2) -> {
    System.out.println(num1 + num2);
};

2.参数小括号精简

//2. 如果接口的方法的参数列表中只有一个参数,则我们可以省略小括号
NoneReturnSingleParameter lambda2 = (num) -> {
    System.out.println(num);
};
//对于上面的Lambda表达式我们进行如下的精简
NoneReturnSingleParameter lambda2Simple = num -> {
    System.out.println(num);
};

3.方法大括号精简

//3. 如果方法体只有一条语句,我们省略大括号
NoneReturnSingleParameter lambda3 = num -> System.out.println(num);

4.大括号精简补充

//4. 如果方法体的唯一一条语句是返回语句,则我们在省略大括号的同时,可以省略return
SingleReturnSingleParameter lambda4 = num -> num;
lambda4.test(10);

5.多参数,有返回值 精简

//5. 如果存在多个参数,同样可以精简
SingleReturnMutipleParameter lambda5 = (num1,num2) -> num1 + num2;
int result = lambda5.test(10, 20);
System.out.println(result);

四、Lambda语法进阶

1.方法引用(普通方法与静态方法)

我们先定义一个函数式接口:MethodReference

@FunctionalInterface
public interface MethodReference {
    int test(int num);
}

在实际应用过程中,一个接口在很多地方都会调用同一个实现,例如:

@Test
public void test01() {
    MethodReference method1 = num -> num * 2;
    MethodReference method2 = num -> num * 2;
    MethodReference method3 = num -> num * 2;

    int res1 = method1.test(10);
    int res2 = method2.test(20);
    int res3 = method3.test(30);
    System.out.println(res1);
    System.out.println(res2);
    System.out.println(res3);

}

这样一来每次都要写上具体的实现方法 a+b,如果需求变更,则每一处实现都需要更改,基于这种情况,可以将后续的是实现更改为已定义的 方法,需要时直接调用就行

语法:
/**
*方法引用:
* 可以快速的将一个Lambda表达式的实现指向一个已经实现的方法
* 方法的隶属者 如果是静态方法 隶属的就是一个类  其他的话就是隶属对象
* 语法:方法的隶属者::方法名
* 注意:
*  1.引用的方法中,参数数量和类型一定要和接口中定义的方法一致
*  2.返回值的类型也一定要和接口中的方法一致
*/
例:
@Test
public void test02() {
    MethodReference method1 = num -> change(num);
    MethodReference method2 = num -> change(num);
    MethodReference method3 = num -> change(num);
    System.out.println(method1.test(10));
    System.out.println(method2.test(20));
    System.out.println(method3.test(30));

}

/*
    * 自定义实现的方法:需要保证参数的数量和类型一定要和接口中定义的方法一致
    * */
public static int change(int num) {
    return num * 2;
}


package com.alan.learn.syntax;
import com.alan.learn.interfaces.LambdaSingleReturnSingleParmeter;
public class Syntax3 {

    public static void main(String[] args) {
        
        LambdaSingleReturnSingleParmeter lambda1=a->a*2;
        LambdaSingleReturnSingleParmeter lambda2=a->a*2;
        LambdaSingleReturnSingleParmeter lambda3=a->a*2;

        //简化
        LambdaSingleReturnSingleParmeter lambda4=a->change(a);

        //方法引用
        LambdaSingleReturnSingleParmeter lambda5=Syntax3::change;
    }

    /**
    * 自定义的实现方法
    */
    private static int change(int a){
        return a*2;
    }
}

此时我们可以利用方法引用的方式继续优化Lambda表达式:

@Test
public void test03() {
    //change是类的静态方法,我们选择使用 类名::方法名
    MethodReference method1 = TestMethodReference::change;
    MethodReference method2 = TestMethodReference::change;
    MethodReference method3 = TestMethodReference::change;
    System.out.println(method1.test(10));
    System.out.println(method2.test(20));
    System.out.println(method3.test(30));

}

2.方法引用(构造方法)

目前有一个实体类

public class Person {
    String name;
    int age;

    public Person() {
        System.out.println("Person 无参构造...");
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person 有参构造...");
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

需求

两个接口,各有一个方法,一个接口的方法需要引用Person的无参构造,一个接口的方法需要引用Person的有参构造 用于返回两个Person对象,例:

public interface PersonCreater1 {
    Person getPerson();
}

public interface PersonCreater2 {
    Person getPerson(String name,int age);
}

那么可以写作:

@Test
public void test04() {
    PersonCreater1 creater1 = Person::new;
    Person person = creater1.getPerson();
    System.out.println(person);
}

@Test
public void test05() {
    PersonCreater2 creater2 = Person::new;
    Person person = creater2.getPerson("zhangsan",24);
    System.out.println(person);
}

注意:是引用无参构造还是引用有参构造 在于接口定义的方法参数

五、综合练习

1.集合排序案例

//将List<Person>集合按照Person的年龄进行排序
@Test
public void test01() {
    List<Person> list = new ArrayList<>();
    list.add(new Person("zhangsan",12));
    list.add(new Person("lisi",32));
    list.add(new Person("wangwu",20));
    list.add(new Person("zhaoliu",5));
    list.add(new Person("fengqi",26));

    //使用lambda表达式进行排序
    list.sort(((o1, o2) -> o2.age - o1.age));
    System.out.println("按照年龄降序以后的list集合:" + list);
}

2.Treeset排序案例

//使用TreeSet进行排序:在TreeSet中如果Comparator返回值是 0 会判断这是两个元素是相同的,会进行去重,我们需要手动实现
@Test
public void test02() {
    TreeSet<Person> set = new TreeSet<>((o1, o2) -> {
        if(o1.age >= o2.age) {
            return -1;
        }else {
            return 1;
        }
    });
    set.add(new Person("zhangsan",12));
    set.add(new Person("lisi",32));
    set.add(new Person("wangwu",20));
    set.add(new Person("zhaoliu",5));
    set.add(new Person("fengqi",26));

    System.out.println(set);

}

3.集合的遍历

/*
     * list.forEach(Consumer<? super E> action)
     * api文档解释:对集合中的每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。
     * 将集合中的每一个元素都带入到接口Consumer的方法accept中,然后方法accept指向我们的引用
     * 输出集合中的所有元素
     * list.forEach(System.out::println);
    * */
@Test
public void test03() {
    List<Person> list = new ArrayList<>();
    list.add(new Person("zhangsan",12));
    list.add(new Person("lisi",32));
    list.add(new Person("wangwu",20));
    list.add(new Person("zhaoliu",5));
    list.add(new Person("fengqi",26));

    //输出全部元素
    list.forEach(System.out::println);

    //输出Person中age为偶数的值
    list.forEach(item -> {
        if (item.age % 2 == 0) {
            System.out.println(item);
        }
    });
}

4.删除集合中满足条件的元素

/*
    * 删除集合中的满足条件的元素 : 将集合中每一个元素带入到接口Predicate的test方法中,如果返回值是true,则删除这个元素
    * */
@Test
public void test04() {
    List<Person> list = new ArrayList<>();
    list.add(new Person("zhangsan",12));
    list.add(new Person("lisi",32));
    list.add(new Person("wangwu",20));
    list.add(new Person("zhaoliu",5));
    list.add(new Person("fengqi",26));

    /*这里我们先按照原来的实现方式:采用迭代器实现
        Iterator<Person> it = list.iterator();
        while (it.hasNext()) {
            Person person = it.next();
            if (person.age > 20) {
                it.remove();  //这里注意不能使用list本身移除,会报并发修改异常
            }
        }*/

    list.removeIf(item -> item.age > 20);
    System.out.println(list);
}

5.开辟一条线程 做一个数字的输出

/**
 * 需求:
 * 开辟一条线程 做一个数字的输出
 */
public class Test {
    public static void main(String[] args) {

        /**
         * 通过Runnable 来实例化线程
         */
        Thread t=new Thread(() -> {
            for(int i = 0;i < 100;i++){
                System.out.println(i);
            }
        });
        t.start();
    }
}

六、系统内置的函数式接口

import java.util.function.*;

/**
 * 系统内置的一些函数式接口
 */
public class FunctionalInterface {
    public static void main(String[] args) {

        // Predicate<T>              :     参数是T 返回值boolean  
        // 在后续如果一个接口需要指定类型的参数,返回boolean时可以指向 Predicate
        //          IntPredicate            int -> boolean
        //          LongPredicate           long -> boolean
        //          DoublePredicate         double -> boolean

        // Consumer<T>               :      参数是T 无返回值(void)
        //          IntConsumer             int ->void
        //          LongConsumer            long ->void
        //          DoubleConsumer          double ->void

        // Function<T,R>             :      参数类型T  返回值R
        //          IntFunction<R>          int -> R
        //          LongFunction<R>         long -> R
        //          DoubleFunction<R>       double -> R
        //          IntToLongFunction       int -> long
        //          IntToDoubleFunction     int -> double
        //          LongToIntFunction       long -> int
        //          LongToDoubleFunction    long -> double
        //          DoubleToLongFunction    double -> long
        //          DoubleToIntFunction     double -> int

        // Supplier<T> : 参数 无 返回值T
        // UnaryOperator<T> :参数T 返回值 T
        // BiFunction<T,U,R> : 参数 T、U 返回值 R
        // BinaryOperator<T> :参数 T、T 返回值 T
        // BiPredicate<T,U> :  参数T、U  返回值 boolean
        // BiConsumer<T,U> :    参数T、U 无返回值
    }
}

常见的函数式接口:

函数式接口参数类型返回类型用途
Consumer消费型接口Tvoid对类型为T的对象应用操作,包含方法:void accept(T t);
Supplier 供给型接口T返回类型为T 的对象,包含方法:T get();
Function 函数型接口TR对类型为T 的对象应用操作,并返回结果。结果R 类型。包含方法:R apply(T t);
Predicate 断定型接口Tboolean确定类型为T 的对象是否满足某约束,并返回boolean值。包含方法boolean test(T t);

七、Lambda闭包

/**
 * Created by FengBin on 2021/8/13 10:26
 * 闭包问题:lambda的闭包可以提升包围变量的生命周期
 * 故我们的局部变量在lambda表达式被引用,此时不会因为方法的结束而销毁,使得我们能在该方法的外部获取到该局部变量的值
 */
public class TestClosure {
    public static void main(String[] args) {
        int result = getNumber().get();
        System.out.println(result);


    }

    public static Supplier<Integer> getNumber() {
        int num = 10;
        //Supplier:无参数,单个单个返回值
        return () -> {
            return num;
        };
    }
}
/**
 * Created by FengBin on 2021/8/13 10:30
 * 对于闭包中变量必须是一个常量,在编译的过程中,自动为其添加final修饰符,故我们对于闭包中的变量不用进行任何的修改,否则会报错
 */
public class TestCosure02 {
    public static void main(String[] args) {
        int num = 10;
        //Consumer 单个参数,没有返回值
        Consumer<Integer> c = item -> {
            System.out.println(num);
        };

        //num++;    不要尝试修改闭包中的变量的值,这样会使得编译报错的!!!
        
        //这里输入的参数并不影响最后的结果,都是返回10
        c.accept(1);


    }
}

输出结果:

10
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值