JDK8新特性-Lambda表达式

JDK8新特性-Lambda表达式

  1. 需求案例

    • 创建一个线程

      //开启一个新的线程
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      System.out.println("新线程中执行的代码" + Thread.currentThread().getName());
                  }
              }).start();
              System.out.println("主线程中的代码" + Thread.currentThread().getName());
              System.out.println("--------------");
      
    • 代码分析

      • Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务的核心内容。
      • 为了指定run方法体,不得不需要Runnable的实现类。
      • 为了省去定义一个Runnable的实现类,不得不使用匿名内部类。
      • 必须覆盖重写抽象的run方法,所有的方法名,方法参数,方法返回值不得不重写一遍,且不能出错。
      • 而实际上,我们只在乎方法体中的代码。
  2. Lambda表达式初体验

    • Lambda表达式是一个匿名函数,可以理解为一段传递的代码

    •  new Thread(()->{
                  System.out.println("新线程lambda表达式..."+Thread.currentThread().getName());
              }).start();
      
    • Lambda表达式的优点是:简化了匿名内部类的使用,语法更加简单。

  3. Lambda表达式规则

    • Lambda省去了面向对象的很多规则,Lambda标准格式由以下组成:

      • (参数类型 参数名称)->{
            代码体;
        }
        //(参数类型 参数名称):参数列表
        //(代码体):方法体
        //(->):分割参数列表和方法体
        
    • Lambda练习1:练习无返回值的Lambda。

      • 定义一个接口 UserService

      • public interface UserService {
            void show();
        }
        
      • 分别使用匿名内部类和Lambda

      • public class Demo03Lambda {
            public static void main(String[] args) {
                goShow(new UserService() {
                    @Override
                    public void show() {
                        System.out.println("show方法执行了...");
                    }
                });
                System.out.println("---------");
                goShow(() -> {
                    System.out.println("Lambda show方法执行了...");
                });
            }
        
            public static void goShow(UserService userService) {
                userService.show();
            }
        }
        
      • 输出

      • show方法执行了...
        ---------
        Lambda show方法执行了...
        
    • Lambda练习2:完成一个有参且有返回值的表达式案例

      • 创建一个用户类

      • public class Person {
            private String name;
            private Integer age;
            private Integer height;
        
            public Person(String name, Integer age, Integer height) {
                this.name = name;
                this.age = age;
                this.height = height;
            }
        
            public Person() {
            }
        
            @Override
            public String toString() {
                return "Person{" +
                        "name='" + name + '\'' +
                        ", age=" + age +
                        ", height=" + height +
                        '}';
            }
        
            public String getName() {
                return name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        
            public Integer getAge() {
                return age;
            }
        
            public void setAge(Integer age) {
                this.age = age;
            }
        
            public Integer getHeight() {
                return height;
            }
        
            public void setHeight(Integer height) {
                this.height = height;
            }
        }
        
      • 在list中保存多个Person对象,分别使用匿名内部类和Lambda根据年龄进行排序

         public static void main(String[] args) {
                List<Person> list = new ArrayList<>();
                list.add(new Person("周杰伦", 33, 175));
                list.add(new Person("刘德华", 43, 185));
                list.add(new Person("周星驰", 35, 177));
                list.add(new Person("郭富城", 23, 170));
        
                Collections.sort(list, new Comparator<Person>() {
                    @Override
                    public int compare(Person o1, Person o2) {
                        return o1.getAge() - o2.getAge();
                    }
                });
        
                for (Person p : list) {
                    System.out.println(p);
                }
        
                System.out.println("------------");
                Collections.sort(list, (Person o1, Person o2) -> {
                    return o1.getAge() - o2.getAge();
                });
        
                for (Person person : list) {
                    System.out.println(person);
                }
            }
        
      • 输出

      • Person{name='郭富城', age=23, height=170}
        Person{name='周杰伦', age=33, height=175}
        Person{name='周星驰', age=35, height=177}
        Person{name='刘德华', age=43, height=185}
        ------------
        Person{name='郭富城', age=23, height=170}
        Person{name='周杰伦', age=33, height=175}
        Person{name='周星驰', age=35, height=177}
        Person{name='刘德华', age=43, height=185}
        
  4. @FunctionalInterface注解修饰的接口只能声明一个抽象方法

    • @FunctionalInterface
      public interface UserService {
          void show();
      }
      
  5. Lambda表达式原理

    • 匿名内部类的本质是在编译的时候生成一个java类和该类的Class文件。XXXX$1.class,可以通过反编译查看到。

    • 反编译 Demo03Lambda1.classjavapcpDemo03Lambda1.class

    • D:\java-thread\out\production\java-thread\com\heiye\jdk\lambda>javap -c -p Demo03Lambda.class
      Compiled from "Demo03Lambda.java"
      public class com.heiye.jdk.lambda.Demo03Lambda {
        public com.heiye.jdk.lambda.Demo03Lambda();
          Code:
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
      
        public static void main(java.lang.String[]);
          Code:
             0: invokedynamic #2,  0              // InvokeDynamic #0:show:()Lcom/heiye/jdk/lambda/service/UserService;
             5: invokestatic  #3                  // Method goShow:(Lcom/heiye/jdk/lambda/service/UserService;)V
             8: return
      
        public static void goShow(com.heiye.jdk.lambda.service.UserService);
          Code:
             0: aload_0
             1: invokeinterface #4,  1            // InterfaceMethod com/heiye/jdk/lambda/service/UserService.show:()V
             6: return
      
        private static void lambda$main$0();
          Code:
             0: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #6                  // String Lambda show方法执行了...
             5: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
      }
      
    • 在这个反编译的源码中,看到了一个静态方法lambdamain0(),这个方法做了什么事情呢?我们通过debug查看以下:

    • image

    • 方法体中的代码是在lambdamain0中执行的。

    • 上面的效果可以理解为如下

    •  public static void main(String[] args) {
              goShow(() -> {
                  System.out.println("Lambda show方法执行了...");
              });
          }
      
          public static void lambda$main$0() {
              userService.show();
          }
      
    • 为了更加直观的理解,我们可以添加 -Djdk.internal.lambda.dumpProxyClasses,加上这个参数会将内部class码到一个文件中。

    • java -Djdk.internal.lambda.dumpProxyClasses 要运行的包名.类名
      
    • 命令执行会有一个文件,然后反编译会得到

    • final class Demo03Lambda$$Lambda$1 implements UserService{
          public void show(){
              	Demo03Lambda.lambda$main$0();
          }
          
          private Demo03Lambda$$Lambda$1(){
          }
      }
      
    • 可以看到匿名内部类实现了UserService接口并重写了show方法,在show方法中调用了Demo03Lambda.lambdamain0()方法,也就是调用了Lambda中的内容。

    • 小结:

      1. 匿名内部类在编译的时候会产生一个class文件。
      2. Lambda表达式在程序运行的时候会形成一个类在类中会新增一个方法,这个方法的方法体就是Lambda中的代码。
      3. 还会形成一个匿名内部类,实现接口,重写抽象方法。
      4. 在接口中重写方法会调用新生成的方法。
  6. Lambda表达式省略写法

    • 在lambda表达式标准写法基础上,可以使用省略写法规则为:

      1. 小括号内的参数类型可以省略。
      2. 如果小括号有且仅有一个参数,则小括号可以省略。
      3. 如果大括号只有一个语句,可以同时省略大括号,return关键字及语句和分号。
    • public static void main(String[] args) {
              goStudent((String name, Integer age) -> {
                  return name + String.valueOf(age);
              });
              //省略写法
              goStudent((name, age) -> name + age;);
              System.out.println("----------");
              goOrder((String name) -> {
                  return name;
              });
              //省略写法
              goOrder(name -> String.valueOf(name));
          }
      
          public static void goStudent(StudentService studentService) {
              studentService.show("张三", 22);
          }
      
          public static void goOrder(OrderService orderService) {
              orderService.show("张三");
          }
      
  7. Lambda表达式使用前提:Lambda表达式非常简洁,但是有几个条件要注意:

    1. 方法参数或局部变量类型必须为接口才能使用Lambda。
    2. 接口中有且仅有一个抽象方法(@FunctionalInterface)。
  8. Lambda和匿名内部类的对比

    1. 所需类型不一样。
      1. 匿名内部类类型可以是类,抽象类,接口。
      2. Lambda需要的类型必须是接口。
    2. 抽象方法的数量不一样。
      1. 匿名内部类所需的接口中的抽象方法数量是随意的。
      2. Lambda表达式所需的接口中只能有一个抽象方法。
    3. 实现原理不一样
      1. 匿名内部类是在编译后形成一个class。
      2. Lambda表达式是在程序运行时动态生成class。
posted @   LilyFlower  阅读(69)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示