• Lambda表达式超详细总结


    Lambda表达式介绍

    Java 8的一个大亮点是引入Lambda表达式,使用它设计的代码会更加简洁。通过Lambda表达式,可以替代我们以前经常写的匿名内部类来实现接口。Lambda表达式本质是一个匿名函数

    体验Lambda表达式

    我们通过一个小例子来体验下Lambda表达式;

    我们定义一个计算接口,只有一个方法add;

    使用原始方式调用接口:

    1. public class LambdaTest {
    2. //定义一个cal接口
    3. interface Cal {
    4. int add(int a, int b);
    5. }
    6. public static void main(String[] args) {
    7. //使用原始方式调用接口
    8. Cal cal = new Cal(){
    9. @Override
    10. public int add(int a, int b) {
    11. return a+b;
    12. }
    13. };
    14. System.out.println("a+b等于:"+cal.add(1,2));
    15. }
    16. }

    这个是我们以前的实现,匿名内部类,然后调用执行;

    使用lambda表达式的方式:

    1. public class LambdaTest {
    2. //定义一个cal接口
    3. interface Cal {
    4. int add(int a, int b);
    5. }
    6. public static void main(String[] args) {
    7. //使用lambda表达式的方式
    8. Cal cal = (int a,int b) ->{
    9. return a+b;
    10. };
    11. System.out.println("a+b等于:" + cal.add(1, 2));
    12. }
    13. }

    匿名内部类,直接改成了:

    Cal cal = (int a,int b) ->{return a+b;};

    简洁多了;

    是不是感觉Lambda表达式挺强大,

    接下来我们来看看Lambda表达式的语法吧;

    Lambda表达式语法

    我们看下这个Lambda表达式:

    Cal cal = (int a,int b) ->{return a+b;};

    这个本质是一个函数;

    一般的函数类似如下:

    1. int add(int a,int b){
    2. return a+b;
    3. }

    有返回值,方法名,参数列表,方法体

    Lambda表达式函数的话,只有参数列表,和方法体;

    ( 参数列表 ) -> { 方法体 }

    说明:

    ( ) :用来描述参数列表;

    { } : 用来描述方法体;

    ->  :Lambda运算符,可以叫做箭头符号,或者goes to

    Lambda表达式语法细讲

    我们搞一个案例,接口方法参数,无参,单个参数,两个参数,有返回值,没有返回值;

    这六种情况都罗列下:

    1. interface If1{
    2. /**
    3. * 无参数无返回值
    4. */
    5. void test();
    6. }
    7. interface If2{
    8. /**
    9. * 单个参数无返回值
    10. * @param a
    11. */
    12. void test(int a);
    13. }
    14. interface If3{
    15. /**
    16. * 两个参数无返回值
    17. * @param a
    18. * @param b
    19. */
    20. void test(int a,int b);
    21. }
    22. interface If4{
    23. /**
    24. * 无参数有返回值
    25. * @return
    26. */
    27. int test();
    28. }
    29. interface If5{
    30. /**
    31. * 单个参数有返回值
    32. * @param a
    33. * @return
    34. */
    35. int test(int a);
    36. }
    37. interface If6{
    38. /**
    39. * 多个参数有返回值
    40. * @param a
    41. * @param b
    42. * @return
    43. */
    44. int test(int a,int b);
    45. }

    我们用Lambda表达式实现:

    1. /**
    2. * 无参数无返回值
    3. */
    4. If1 if1 = ()->{
    5. System.out.println("无参数无返回值...");
    6. };
    7. if1.test();
    8. /**
    9. * 单个参数无返回值
    10. * @param a
    11. */
    12. If2 if2 = (int a)->{
    13. System.out.println("单个参数无返回值,a="+a);
    14. };
    15. if2.test(3);
    16. /**
    17. * 两个参数无返回值
    18. * @param a
    19. * @param b
    20. */
    21. If3 if3 = (int a,int b)->{
    22. System.out.println("单个参数无返回值,a+b="+(a+b));
    23. };
    24. if3.test(3,2);
    25. /**
    26. * 无参数有返回值
    27. * @return
    28. */
    29. If4 if4 = ()->{
    30. System.out.print("无参数有返回值...");
    31. return 100;
    32. };
    33. System.out.println(if4.test());
    34. /**
    35. * 单个参数有返回值
    36. * @param a
    37. * @return
    38. */
    39. If5 if5 = (int a)->{
    40. System.out.print("单个参数有返回值...");
    41. return a;
    42. };
    43. System.out.println(if5.test(200));
    44. /**
    45. * 多个参数有返回值
    46. * @param a
    47. * @param b
    48. * @return
    49. */
    50. If6 if6 = (int a,int b)->{
    51. System.out.print("多个参数有返回值...");
    52. return a+b;
    53. };
    54. System.out.println(if6.test(2,3));

    运行输出:

    1. 无参数无返回值...
    2. 单个参数无返回值,a=3
    3. 单个参数无返回值,a+b=5
    4. 无参数有返回值...100
    5. 单个参数有返回值...200
    6. 多个参数有返回值...5

    Lambda表达式精简语法

    Lambda语法注意点:

    1,参数类型可以省略

    2,假如只有一个参数,()括号可以省略

    3,如果方法体只有一条语句,{}大括号可以省略

    4,如果方法体中唯一的语句是return返回语句,那省略大括号的同事return也要省略

    改写实例:

    1. /**
    2. * 无参数无返回值
    3. */
    4. If1 if1 = () -> System.out.println("无参数无返回值...");
    5. if1.test();
    6. /**
    7. * 单个参数无返回值
    8. * @param a
    9. */
    10. If2 if2 = a -> System.out.println("单个参数无返回值,a="+a);
    11. if2.test(3);
    12. /**
    13. * 两个参数无返回值
    14. * @param a
    15. * @param b
    16. */
    17. If3 if3 = (a,b) -> System.out.println("单个参数无返回值,a+b="+(a+b));
    18. if3.test(3,2);
    19. /**
    20. * 无参数有返回值
    21. * @return
    22. */
    23. If4 if4 = () -> 100;
    24. System.out.println(if4.test());
    25. /**
    26. * 单个参数有返回值
    27. * @param a
    28. * @return
    29. */
    30. If5 if5 = a -> {
    31. System.out.print("单个参数有返回值...");
    32. return a;
    33. };
    34. System.out.println(if5.test(200));
    35. /**
    36. * 多个参数有返回值
    37. * @param a
    38. * @param b
    39. * @return
    40. */
    41. If6 if6 = (a,b) -> a+b;
    42. System.out.println(if6.test(2,3));

    方法引用

    有时候多个lambda表达式实现函数是一样的话,我们可以封装成通用方法,以便于维护;

    这时候可以用方法引用实现:

    语法是:对象::方法

    假如是static方法,可以直接 类名::方法

    实例如下:

    1. interface If5{
    2. /**
    3. * 单个参数有返回值
    4. * @param a
    5. * @return
    6. */
    7. int test(int a);
    8. }
    9. public int test(int a){
    10. return a-2;
    11. }
    12. public static void main(String[] args) {
    13. LambdaTest3 lambdaTest3 = new LambdaTest3();
    14. If5 if5 = lambdaTest3::test;
    15. System.out.println("单个参数有返回值:"+if5.test(3));
    16. }

    构造方法引用

    如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,

    那么就可以使用构造方法引用;

    语法:类名::new

    实例:

    先定义一个Dog实体,实现无参和有参构造方法;

    1. public class Dog {
    2. private String name;
    3. private int age;
    4. public Dog() {
    5. System.out.println("无参构造方法");
    6. }
    7. public Dog(String name, int age) {
    8. System.out.println("有参构造方法");
    9. this.name = name;
    10. this.age = age;
    11. }
    12. public String getName() {
    13. return name;
    14. }
    15. public void setName(String name) {
    16. this.name = name;
    17. }
    18. public int getAge() {
    19. return age;
    20. }
    21. public void setAge(int age) {
    22. this.age = age;
    23. }
    24. @Override
    25. public String toString() {
    26. return "Dog{" +
    27. "name='" + name + '\'' +
    28. ", age=" + age +
    29. '}';
    30. }
    31. }

    在定义两个接口:

    1. interface DogService{
    2. Dog getDog();
    3. }
    4. interface DogService2{
    5. Dog getDog(String name,int age);
    6. }

    测试:

    1. //普通方式
    2. DogService dogService=()->{
    3. return new Dog();
    4. };
    5. dogService.getDog();
    6. //简化方式
    7. DogService dogService1=()->new Dog();
    8. dogService1.getDog();
    9. //构造方法引用
    10. DogService dogService2=Dog::new;
    11. dogService2.getDog();
    12. // 构造方法引用 有参
    13. DogService2 dogService3=Dog::new;
    14. dogService3.getDog("小米",11);

    执行结果:

    1. 无参构造方法
    2. 无参构造方法
    3. 无参构造方法
    4. 有参构造方法

    综合实例

    下面我们通过几个lambda操作集合的综合实例,来深入体验下Lambda表达式用法;

    Lambda表达式排序:

    1. //定义一个List集合
    2. List list=new ArrayList<>();
    3. //往集合中添加值
    4. list.add(new Dog("aa",1));
    5. list.add(new Dog("bb",4));
    6. list.add(new Dog("cc",3));
    7. list.add(new Dog("dd",2));
    8. list.add(new Dog("ee",5));
    9. //排序
    10. System.out.println("lambda集合排序:");
    11. list.sort((o1, o2) -> o1.getAge()-o2.getAge());
    12. System.out.println(list);
    13. // 遍历集合
    14. System.out.println("lambda遍历集合:");
    15. list.forEach(System.out::println);

    运行输出:

    1. lambda集合排序:
    2. [Dog{name='aa', age=1}, Dog{name='dd', age=2}, Dog{name='cc', age=3}, Dog{name='bb', age=4}, Dog{name='ee', age=5}]
    3. lambda遍历集合:
    4. Dog{name='aa', age=1}
    5. Dog{name='dd', age=2}
    6. Dog{name='cc', age=3}
    7. Dog{name='bb', age=4}
    8. Dog{name='ee', age=5}

    我们来分析下集合的sort方法,

    sort方法里有一个Comparator接口,再点进去看下:

    我们通过lambda就可以轻松实现排序:

    (o1,o2)->o1.getAge()-o2.getAge()

    再看下集合的forEach方法,点进去:

    有个消费者Consumer接口,再点进去:

    接口里有个接口参数的accept的方法;

    所以我们直接方法引用 直接输出每次的遍历值即可;

    System.out::println

    @FunctionalInterface注解

    前面我们会发现Consumer接口,Comparator接口都有@FunctionalInterface注解;

    这个注解是函数式接口注解,所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法。

    这种类型的接口也称为SAM接口,即Single Abstract Method interfaces

    特点:

    1、接口有且仅有一个抽象方法

    2、允许定义静态方法

    3、允许定义默认方法

    4、允许java.lang.Object中的public方法

    5、该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错

    实例:

    1. // 正确的函数式接口
    2. @FunctionalInterface
    3. public interface TestInterface {
    4. // 抽象方法
    5. public void sub();
    6. // java.lang.Object中的public方法
    7. public boolean equals(Object var1);
    8. // 默认方法
    9. public default void defaultMethod(){
    10. }
    11. // 静态方法
    12. public static void staticMethod(){
    13. }
    14. }
    15. // 错误的函数式接口(有多个抽象方法)
    16. @FunctionalInterface
    17. public interface TestInterface2 {
    18. void add();
    19. void sub();
    20. }

    系统内置函数式接口

    Java8的推出,是以Lambda重要特性,一起推出的,其中系统内置了一系列函数式接口;

    在jdk的java.util.function包下,有一系列的内置函数式接口:

    比如常用的Consumer,Comparator,Predicate,Supplier等;

    Lambda表达式视频教程

    B站视频教程在线地址:

    Java8 Lambda表达式视频教程(无废话版)_哔哩哔哩_bilibili

  • 相关阅读:
    Ext Direct 开发全介绍
    算法导论第一章——算法在计算中的应用
    基于云计算与深度学习的常见作物害虫识别系统的设计与实现
    我代码就加了一行 log 日志,结果引发了 P1 的线上事故
    码云gitee+宝塔webhook实现同步代码
    【建议背诵】软考高项考试案例简答题汇总~(7)
    Python 图形化界面基础篇:使用网格布局( Grid Layout )排列元素
    1230: 蜂巢
    C++中有哪些常用的算法和数据结构?
    Libgdx游戏开发(6)——游戏暂停
  • 原文地址:https://blog.csdn.net/weixin_55076626/article/details/127830494