IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 聊一下JDK8中,Lambda表达式实现原理 -> 正文阅读

[移动开发]聊一下JDK8中,Lambda表达式实现原理

概念

Lambda表达式,也称函数编程,百度百科概念:

Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。

从上面的概念上看,Lambda表达式其实就是一个没有名字的函数。但有一个问题,Java并没有函数的这个概念,那JDK8之后所说的函数是啥意思呢?

答案:Java中函数就是函数接口中抽象方法的实现逻辑

再具体点:函数接口抽象方法的方法体

表现形式:

(parameters) -> expression
或
(parameters) ->{ statements; }

以线程Runable接口为例子

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

JDK8之前,普通匿名内部类实现

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println(".....");
    }
}).start();

JDK8之后,Lambda实现

new Thread(()-> {System.out.println("....");}).start();

Thread类传入的对象就是lambda表达式对象:()-> {System.out.println("....");},这也就概念上讲的Java函数。

如果你认真观察,你就会发现Thread接收参数是Runable接口的run方法实现。

案例分析

需求:定义一个函数接口,使用常规方式、匿名内部类方式与lambda方式实现

定义一个函数接口:

@FunctionalInterface
public interface MyInterface {
    void sayHi();
}

方式1:常规的实现方式

//定义一个接口实现类
public class MyInterfaceImpl implements  MyInterface{
    @Override
    public void sayHi() {
        System.out.println("hello lambda....");
    }
}
public class InterfaceDemo {

    //定义函数接口方法
    public void hi(MyInterface myInterface){
        myInterface.sayHi();
    }
    public static void main(String[] args) {
        InterfaceDemo demo = new InterfaceDemo();
        demo.hi(new MyInterfaceImpl());
    }
}

方式2:匿名内部类实现方式

public class InterfaceDemo {
    //定义函数接口方法
    public void hi(MyInterface myInterface){
        myInterface.sayHi();
    }
    public static void main(String[] args) {
        InterfaceDemo demo = new InterfaceDemo();
        demo.hi(new MyInterface() {
            @Override
            public void sayHi() {
                System.out.println("hello lambda...");
            }
        });
    }
}

方式3:使用Lambda实现方式

public class InterfaceDemo {
    //定义函数接口方法
    public void hi(MyInterface myInterface){
        myInterface.sayHi();
    }
    public static void main(String[] args) {
        InterfaceDemo demo = new InterfaceDemo();
        //函数逻辑
        demo.hi(()-> System.out.println("hello lambda..."));
    }
}

2种方式实现的效果一样,使用lambda方式实现更加简洁方便。同时也有点无厘头,特别是没有学习或完全掌握Lambda语法的同学,说不出的奇怪感。那Lambda究竟是啥回事?

原理

要弄懂这个lambda表达式实现原理,需要借助JDK自带的Java class文件分解器:javap 工具

步骤1:检查java的环境变量

注意:必须配置了JAVA环境变量

?步骤2:查看字节码基本信息

进入到项目字节码(.class)文件所在目录,演示案例使用的maven结构项目,所以进入target目录。

//输入命令
javap -p InterfaceDemo.class

?观察返回的字节码信息,有个特殊的方法

private static void lambda$main$0();

看到这,可以大胆做推断:当类中使用lambda 表达式,编译器会自动创建一个私有的,静态的,格式为: lambda$方法名$序号?方法。

步骤3:验证?lambda$方法名$序号方法?推断

这里我们可以确认推断是否正确, 在InterfaceDemo? 类上加上相同方法签名的方法

public class InterfaceDemo {
    //定义函数接口方法
    public void hi(MyInterface myInterface){
        myInterface.sayHi();
    }
    public static void main(String[] args) {
        InterfaceDemo demo = new InterfaceDemo();
        demo.hi(()-> System.out.println("hello lambda..."));
    }

    //编译前自定义名称一模一样的方法
    private static void lambda$main$0(){

    }
}

然后执行run操作

编译没问题,但一执行马上抛异常,异常明显指定跟新增的方法有关。

到这,证明上面推断是对的。

步骤4:大胆猜测,小心求证

上面推断是正确的, 那我们不禁要问,lambda$main$0?方法是干什么的,跟lambda表达式有啥关系?回想下,刚刚案例分析中提到实现方式:?

常规方式:定义一个新类:MyInterfaceImpl?实现接口MyInterface

匿名内部类方式:定义一个匿名的类,实现接口MyInterface

上面2种都需要一个接口实现类,Lambda表达式能实现相同功能,那是否可以推断lambda表达式的实现方式也必须要有一个接口实现类呢??完全可以取验证一下:

Lamdba 表达方式:

在执行InterfaceDemo 类main方法之前, 先配置VM?options,目的:JVM运行时将动态创建类字节码对象打印处理。

-Djdk.internal.lambda.dumpProxyClasses

?运行之后得到结果

?用idea打开:

import java.lang.invoke.LambdaForm.Hidden;

// $FF: synthetic class
final class InterfaceDemo$$Lambda$14 implements MyInterface {
    private InterfaceDemo$$Lambda$14() {
    }
    @Hidden
    public void sayHi() {
        InterfaceDemo.lambda$main$0();
    }
}

?惊讶发现,lambda语法确实创建了一个MyInterface接口实现类,名字为:InterfaceDemo$$Lambda$14,那么

//定义
public void hi(MyInterface myInterface){
	myInterface.sayHi();
}
//调用
demo.hi(()-> System.out.println("hello lambda..."));

可以认为等价于

//调用
demo.hi(new InterfaceDemo$$Lambda$14());

?步骤5:真相大白

到这,离真相大白就剩下一步之遥了:

最后一个问题,InterfaceDemo$$Lambda$14()类中sayHi方法是怎么重写的?

//实现类:InterfaceDemo$$Lambda$14
@Hidden
public void sayHi() {
	InterfaceDemo.lambda$main$0();
}

内部类InterfaceDemo$$Lambda$14重写接口方法sayHi直接调用:InterfaceDemo的lambda$main$0(); 方法

lambda$main$0 方法是编译器自动生成的。那lambda$main$0 方法的方法体是啥,实现了啥逻辑?

再次使用javap命令查看字节码对象与编译流程(此次查看更详细的编译过程)

javap -p -v -c InteraceDemo.class

这种命令查看信息更全,截取重要的信息

 private static void lambda$main$0();
    descriptor: ()V
    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #8                  // String hello lambda...
         5: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 10: 0
}

上面的代码是类似汇编语言,从旁边的注释上能看出lambda$main$0方法大概实现了。

lambda$main$0 方法在方法体重,调用了PrintStream类的println方法,打印的值是字符:hello lambda...? ?也即: System.out.println("hello lambda....");

哇塞~ 真相大白了。

步骤6:总结

我们从头梳理一下:

lambda表达式写法

public class InterfaceDemo {
    //定义函数接口方法
    public void hi(MyInterface myInterface){
        myInterface.sayHi();
    }
    public static void main(String[] args) {
        InterfaceDemo demo = new InterfaceDemo();
        demo.hi(()-> System.out.println("hello lambda..."));
    }
}

编译之后的写法:

public class InterfaceDemo {
    //定义函数接口方法
    public void hi(MyInterface myInterface){
        myInterface.sayHi();
    }
    
    //编译器自动添加静态方法,方便动态生成的内部类调用
    private static void lambda$main$0(){
        System.out.println("hello lambda...");
    }

    public static void main(String[] args) {
        
        //编译器动态创建的局部内部类
        final class InterfaceDemo$$Lambda$14 implements MyInterface {
            private InterfaceDemo$$Lambda$14() {
            }
            @LambdaForm.Hidden
            public void sayHi() {
                InterfaceDemo.lambda$main$0();
            }
        }
        
        InterfaceDemo demo = new InterfaceDemo();
        demo.hi(new InterfaceDemo$$Lambda$14());
    }
}

总结

Lambda表达式实现原理:

1>JDK编译时会给使用lambda表达式的类中添加一个私有的,静态的方法, 格式:lambda$方法名$序号?

2>JDK编译时动态创建一个局部内部类,该类实现了函数接口,重写函数接口唯一的抽象方法。

3>局部内部类重写接口抽象方法:直接调用步骤1中新增的私有的,静态的方法。

最后的最后,用大白话讲请求lambda表达式原理:

Lambda表达式就一个语法糖,本质还是常规的接口实现,只是将实现过程做了简化。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-05-11 16:33:45  更:2022-05-11 16:34:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 1:32:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码