首页 > 编程语言 >18.Java Lambda 表达式(Lambda 表达式练习与原理分析、@FunctionalInterface 注解)

18.Java Lambda 表达式(Lambda 表达式练习与原理分析、@FunctionalInterface 注解)

时间:2024-12-15 15:27:05浏览次数:5  
标签:Java my class Lambda com public 表达式 lambda

一、问题引入

1、问题案例
  • 开启一个新的线程,指定线程要执行的任务
new Thread(new Runnable() {
    public void run() {
        System.out.println("Hello World");
    }
}).start();
2、问题分析
  1. Thread 类需要一个 Runnable 接口作为参数,其中抽象方法 run 是用来指定线程的核心任务

  2. 为了实现 run 方法的方法体,不得不需要 Runnable 实现类

  3. 为了省去定义一个 Runnable 实现类,不得不使用匿名内部类

  4. 必须重写 run 方法,那么方法名称、返回值、参数列表等都不得不重写,而实际上,我们只在乎方法体


二、Lambda 表达式引入

1、初体验
  • Lambda 表达式是一个匿名函数,可以理解为一段可传递的代码
new Thread(() -> {
    System.out.println("Hello Lambda");
}).start();
2、基本介绍
  1. Lambda 表达式简化了匿名内部类冗余的语法使用,语法更简单

  2. Lambda 表达式的标准格式如下,由三个部分组成

(【参数类型】 【参数名称】) -> {
    【方法体】;
}

三、Lambda 表达式练习

1、无参无返回值
  • 定义 UserService 接口
package com.my.lambda.service;

public interface UserService {
    void show();
}
  • 测试
package com.my.lambda.test;

import com.my.lambda.service.UserService;

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

        // 匿名内部类
        goShow(new UserService() {
            @Override
            public void show() {
                System.out.println("Hello Show");
            }
        });

        // Lambda 表达式
        goShow(() -> {
            System.out.println("Lambda Hello Show");
        });
    }

    public static void goShow(UserService userService) {
        userService.show();
    }
}
2、有参有返回值
  • 定义 User 类
package com.my.lambda.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private int age;
}
  • 测试
package com.my.lambda.test;

import com.my.lambda.entity.User;

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

public class LambdaTest3 {
    public static void main(String[] args) {
        ArrayList<User> users = new ArrayList<>();
        users.add(new User("jack", 10));
        users.add(new User("smith", 15));
        users.add(new User("tom", 5));

//        // 匿名内部类
//        Collections.sort(users, new Comparator<User>() {
//            @Override
//            public int compare(User o1, User o2) {
//                return o1.getAge() - o2.getAge();
//            }
//        });

        // Lambda 表达式
        Collections.sort(users, (User o1, User o2) -> {
            return o1.getAge() - o2.getAge();
        });

        Iterator<User> iterator = users.iterator();
        while (iterator.hasNext()) {
            User user = iterator.next();
            System.out.println(user);
        }
    }
}

四、@FunctionalInterface 注解

1、基本介绍
  • 被该注解修饰的接口只能声明一个抽象方法
2、应用场景
  • 使用 Lambda 表达式实现的接口只能存在一个抽象方法

五、原理分析

1、XJad
(1)XJad 下载
  • 下载地址:http://www.ucbug.com/soft/129590.html
(2)XJad 安装
  • 解压缩
(3)XJad 使用
  • 直接将 class 文件拖入
2、匿名内部类原理
  • 匿名内部类的本质是在编译时生成一个 class 文件,名称为 【XXX】$【XXX】.class(例如:LambdaTest1$1.class)

  • 可以使用反编译工具 XJad 进行查看

// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space 
// Source File Name:   LambdaTest1.java

package com.my.lambda.test;

import java.io.PrintStream;

// Referenced classes of package com.my.lambda.test:
//			LambdaTest1

static class LambdaTest1$1 implements Runnable {

	public void run() {
		System.out.println("Hello World");
	}

	LambdaTest1$1() {}
}
3、JDK 的反编译工具
  • 写有 Lambda 表达式的 class 文件,无法使用 XJad 进行查看

  • 可以使用 JDK 自带的 javap 对class 文件进行反编译操作

javap -c -p 【class 文件】
  • 生成一个 Lambda 表达式对应的 class 文件
java -Djdk.internal.lambda.dumpProxyClasses 【全类名】
4、Lambda 表达式原理
  • 使用 javap 对 LambdaTest2.class 文件进行反编译操作
D:\javaCode\JDK8Test\target\classes\com\sunke\lambda\test>javap -c -p LambdaTest2.class
Compiled from "LambdaTest2.java"
public class com.my.lambda.test.LambdaTest2 {
  public com.my.lambda.test.LambdaTest2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/sunke/lambda/test/LambdaTest2$1
       3: dup
       4: invokespecial #3                  // Method com/sunke/lambda/test/LambdaTest2$1."<init>":()V
       7: invokestatic  #4                  // Method goShow:(Lcom/sunke/lambda/service/UserService;)V
      10: invokedynamic #5,  0              // InvokeDynamic #0:show:()Lcom/sunke/lambda/service/UserService;
      15: invokestatic  #4                  // Method goShow:(Lcom/sunke/lambda/service/UserService;)V
      18: return

  public static void goShow(com.my.lambda.service.UserService);
    Code:
       0: aload_0
       1: invokeinterface #6,  1            // InterfaceMethod com/sunke/lambda/service/UserService.show:()V
       6: return

  private static void lambda$main$0();
    Code:
       0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #8                  // String Lambda Hello Show
       5: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}
  • 上面的效果可以这样理解,在类中生产了一个新的方法 lambda$main$0
package com.my.lambda.test;

import com.my.lambda.service.UserService;

public class LambdaTest2 {
    public static void main(String[] args) {
        ---
    }

    public static void goShow(UserService userService) {
        userService.show();
    }

    private static void lambda$main$0() {
        System.out.println("Lambda Hello Show");
    }
}
  • 生成 LambdaTest2.class 中 Lambda 表达式对应的 class 文件(注意目录)
D:\javaCode\JDK8Test\target\classes>java -Djdk.internal.lambda.dumpProxyClasses com.my.lambda.test.LambdaTest2
Hello Show
Lambda Hello Show
  • 使用 XJad 查看 LambdaTest2$$Lambda$1.class 文件
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space 

package com.my.lambda.test;

import com.my.lambda.service.UserService;

// Referenced classes of package com.my.lambda.test:
//			LambdaTest2

final class LambdaTest2$$Lambda$1 implements UserService {

	public void show() {
		LambdaTest2.lambda$main$0();
	}

	private LambdaTest2$$Lambda$1() {}
}
  • 生成一个匿名内部类,实现 UserService 接口,重写 show 方法并调用了 LambdaTest2.lambda$main$0()
package com.my.lambda.test;

import com.my.lambda.service.UserService;

public class LambdaTest2 {
    public static void main(String[] args) {
        goShow(new UserService() {
            @Override
            public void show() {
                LambdaTest2.lambda$main\$0();
            }
        });
    }

    public static void goShow(UserService userService) {
        userService.show();
    }

    private static void lambda$main$0() {
        System.out.println("Lambda Hello Show");
    }
}
5、小结
(1) 匿名内部类的本质
  • 编译时生成一个 class 文件
(2)Lambda 表达式的本质
  • 在类中生成一个新方法,该方法的参数列表和方法体就是 Lambda 表达式的参数列表和方法体

  • 在类中生成一个匿名内部类,实现接口并重写抽象方法,重写的抽象方法会调用新生成的方法


六、Lambda 表达式简写

1、简写规则
  • 小括号内的参数类型可以省略

  • 如果小括号内有且仅有一个参数,那么可以省略小括号

  • 如果大括号内有且仅有一条语句,那么可以同时省略大括号、return 关键字和语句分号

2、测试
  • 定义 Student 接口
package com.my.lambda.service;

@FunctionalInterface
public interface StudentService {
    String say(String name, int age);
}
  • 定义 Teacher 接口
package com.my.lambda.service;

@FunctionalInterface
public interface TeacherService {
    void say(String name);
}
  • 测试
package com.my.lambda.test;

import com.my.lambda.service.StudentService;
import com.my.lambda.service.TeacherService;

public class LambdaTest4 {
    public static void main(String[] args) {
        goStudentSay((name, age) -> "我是学生,我的名字是" + name + ", 我今年 + " + age + " 岁");

        goTeacherSay(name -> System.out.println("我是老师,我的名字是" + name));
    }

    public static void goStudentSay(StudentService studentService) {
        System.out.println(studentService.say("jack", 20));
    }

    public static void goTeacherSay(TeacherService teacherService) {
        teacherService.say("tom");
    }
}

七、总结

1、Lambda 表达式的使用前提
  • 方法的参数类型或变量的类型必须为接口

  • 接口中有且仅有一个抽象方法

2、Lambda 表达式对比匿名内部类
  • 类型不同

    • 匿名内部类所需类型可以是类、抽象类、接口

    • Lambda 表达式所需类型必须是接口

  • 抽象方法数量不同

    • 匿名内部类对抽象方法数量没有要求

    • Lambda 表达式所需的接口中只能存在一个抽象方法

  • 实现原理不同

    • 匿名内部类在编译后生成 class 文件

    • Lambda 表达式在程序运行时动态生成 class 文件

标签:Java,my,class,Lambda,com,public,表达式,lambda
From: https://blog.csdn.net/weixin_52173250/article/details/144487575

相关文章

  • 2025最新Java八股文(完整版)
     JAVA基础八股文问:java中序列化是怎么实现的呢?1.实现Serializable接口,就会实现数据序列化的效果。2.调用json做序列化。(就比如:Jackson,fastjson等等)3.实现Enternalizable接口,就可以实现反序列化的效果。问:java的流有哪些呢?从方向方面,主要就是输入流和输出流。从单位方......
  • JAVA学习-练习试用Java实现“使用嵌套循环打印出一个5x5的星号矩阵”
    问题:创建一个Java程序,使用嵌套循环打印出一个5x5的星号矩阵。解答思路:以下是一个Java程序,它使用嵌套循环打印出一个5x5的星号矩阵:publicclassStarMatrix{publicstaticvoidmain(String[]args){intsize=5;//矩阵大小for(inti=0......
  • JAVA学习-练习试用Java实现“从用户输入获取一个整数n,并打印出一个n*n的空心正方形”
    问题:编写一个Java程序,从用户输入获取一个整数n,并打印出一个n*n的空心正方形。解答思路:以下是一个Java程序,它从用户输入获取一个整数'n',然后打印出一个'n*n'的空心正方形:importjava.util.Scanner;publicclassHollowSquare{publicstaticvoidmain(String[]ar......
  • Java——网络编程(上)
    1计算机网络(作用资源共享和信息传递)(计算机网络组成——>硬件——>计算机设备,外部设备,通信线路软件——>网络操作系统,网络管理软件,网络通信协议)计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理......
  • Java——网络编程(中)—TCP通讯(下)
    1双向通讯—创建服务端(双向通信是指通信双方中,任何一方都可为发送端,任何一方都可为接收端)(1创建ServerSocket对象,accept()返回socket)(2双向通讯——>也要创建键盘输入对象)(3通过与客户端对应的Socket对象获取输入流对象)(4通过与客户单对应的Socket对象获取输......
  • Y20030002Java+Jsp+Servlet+MySQL的问卷调查小程序的设计与实现(附源码 配置 文档)
    Java+Servlet+MySQL的问卷调查小程序的设计与实现1.摘要2.系统功能分析3.系统功能结构图4.界面展示5.源码获取1.摘要本系统借助于微信小程序的便捷性和普及性,为用户提供了一个高效、易用的在线问卷调查平台。通过利用微信小程序的方便性和流行性,这个系统为用户打造......
  • 社区生活超市系统|Java|SSM|JSP| 
                  【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SSM、Mybatis-Plus、JSP、jquery,html5⃣️数据库可视化工具:navicat6⃣️服务器:SpringBoot自带apachetom......
  • javapackager JavaFx 打包 成exe 桌面应用 程序
    JavaFx打包成exe桌面应用程序java开发的GUI交付一般就是windows平台一般就是exe,还是比较多流程,依赖环境1.maven所有依赖jar都打包成一个jar,pom增加插件命令mvnassembly:assembly<plugin><artifactId>maven-assembly-plugin</artifactId>......
  • 《Java核心技术I》Swing用户界面组件
    Swing和模型-视图-控制器设计模式用户界面组件各个组成部分,如按钮,复选框,文本框或复杂的树控件,每个组件都有三个特征:内容,如按钮的状态,文本域中的文本。外观,颜色,大小等。行为,对事件的反映。Swing设计者采用了一种很有名的设计模式:MVC模型(model):存储内容。视图(......
  • 《Java核心技术I》Swing布局管理器
    布局管理概述Java开发环境提供了GUI生成器,但要弄清底层原理,也需要手动调整。布局管理器回顾之前程序,按钮包含在JPanel中,用流布局管理器(flowlayoutmanager)管理。自动换行,按钮总是位于面板中央。组件放在容器中,布局管理器绝定容器组件的位置和大......