Lambda表达式的使用、简写与原理深入理解

简介: Lambda表达式的使用、简写与原理深入理解

jdk8之所以引入Lambda表达式,是受到函数式编程的启发,将其以一种特殊的方式引入至java的面向对象编程中。 好处是在很多场景下可以大大简化编程,然而其引入的代价则是 java编译器 和 jvm虚拟机都需要做额外的工作以适应这种新的“函数式”编程语法。

在java编程中,有些一次性临时使用的对象方法,我们可以使用匿名内部类的方式来进行简化。Lambda表达式的一个核心想法就是,借用函数式编程思想和语法进行更彻底的简化,即只需要在代码中说明要做什么即可,编译器可以自动进行类、对象方法中的匹配,不需要再写额外的 new overwride 等等冗余的代码。(函数式编程:别给我扯那些那些类模板、对象属性方法啥的,直接告诉我要做什么,好吗?)

一 Lambda表达式初体验

在上面的例子中,直观的能体会Lambda表达式的作用。简化了什么内容呢?

new 关键词、匿名内部类实现的接口名称、方法签名 、@Override 多余的{} ; 等。

这种写法实在是非常清爽。这能够感受到Lambda表达式的简化,其实这也是Lambda表达式最终极的简化形态。

二 原理深入

那么其中的原理是什么呢?

为什么仅仅只需要声明“做什么事情”即可让编译器无歧义的理解代码呢?

这其中编译之后的class文件是如何的呢?

在jvm层面是否违背了面向对象调用方法运行呢?jvm运行的时候又是如何的呢?

从匿名内部类开始理解

我们可以从匿名内部类开始理解。在面向对象编程过程中我们往往是将我们要做的事情封装在一个方法中,使用方法之前可能要通过对象来进行调用,而要产生对象则必须通过类模板来进行创建,其中经历了两次包装。而匿名内部类则让我们免于写类模板,减少了一次复杂的包装。先看看匿名内部类的原理:

package org.example.test;


public interface MyInterface {
    public abstract void run(String str);
}
package org.example.test;

public class AnonymousTest {
    public static void main(String[] args) {
        test(new MyInterface() {
            @Override
            public void run(String str) {
                System.out.println(str);
            }
        });
    }

    public static void test(MyInterface oneObject){
        oneObject.run("hello");
    }
}

定义MyInterface,同时AnonymousTest类中的test方法接收一个MyInterface的实现对象,该实现对象采用匿名内部类的写法。

编译AnonymousTest,输出如下,其中发现多了一个AnonymousTest$1.class文件。

反编译查看AnonymousTest和AnonymousTest$1.class。

发现了一些端倪。猜测如下:

编译器帮我们自动生成了一个类,且自动实现了MyInterface接口重写了run方法,方法体的内容就是我们在匿名内部类中重写的方法。

AnonymousTest中main方法中test方法调用时,传递了一个对象,这个对象理应是AnonymousTest$1对象,不过这个名字好像不对(猜测是反编译工具的一些处理不够完善,不过这个“new 1()”倒是可以猜测到一些端倪。)

想进一步验证,上字节码!!!

AnonymousTest的main方法和test方法字节码解读:

从main方法的第0~4行中发现,的确是新建了一个AnonymousTest$1对象并执行了初始化,第7行中将刚刚创建出来的AnonymousTest$1对象的引用传递给test方法,调用结束后直接返回。

test方法:第0行将main方法传递进来的AnonymousTest$1对象的引用入操作数栈,第1行加载“hello”字符串,第3行以多态的形式调用MyInterface接口中的run方法,也就是动态调用,程序执行的过程中将会调用AnonymousTest$1对象的run方法。

再看AnonymousTest$1对象的run方法,将方法入参(String str)放入操作数栈,执行打印控制台方法。

完美!!! 到此分析结束(字节码yyds)。

总结:

匿名内部类的确是编译器帮我们自动生成了一个匿名类(这个匿名只是相对于程序员而言的,相对于编译器和jvm是有名字的,该类为xxx$x的格式。其中xxx为匿名内部类所在类的类名,x表示是这个类中的第几个类,从1开始(很显然不能从0开始,因为0一般表示“本”“自己”的意思,这个序号不能分配给其他类也是避免引起混淆,也可以类比java方法中的局部变量表中的索引0代表本对象的引用))。

编译器会将原始匿名内部类的写法转换为标准的传递对象引用的写法。然后交由jvm执行。

很显然,匿名内部类只是一个语法层面的优化,为了避免程序员多写一个类,创造了一种新的语法,然后将创建类的过程交给了编译器。对于jvm来说,其实并没有发生任何的变化。最终的结果就是程序员少写了一点代码,编译器做了一些额外的工作,总工作量其实并没有减少。(不过只要程序员写得少了,那就有这样做的必要,毕竟编译器做额外的重复工作并无所谓。)


目录
相关文章
|
SQL 关系型数据库 数据库
学习分布式事务Seata看这一篇就够了,建议收藏
学习分布式事务Seata看这一篇就够了,建议收藏
19483 2
|
计算机视觉
YOLO 目标检测 识别框不显示文字标签(已解决)
YOLO 目标检测 识别框不显示文字标签(已解决)
|
弹性计算 数据可视化 Ubuntu
基于阿里云ECS搭建FISCO-BCOS区块链
用ubuntu操作系统下的ECS搭建FISCO-BCOS节点,开发区块链网络
基于阿里云ECS搭建FISCO-BCOS区块链
|
安全 网络协议 网络安全
数字防线之下:网络安全与信息安全的当代挑战
在数字化时代的浪潮中,网络安全与信息安全成为了维系社会稳定和保护个人隐私的重要基石。本文将深入探讨网络安全漏洞的形成、加密技术的发展以及提升公众安全意识的重要性。通过分析近期的网络攻击案例、介绍前沿的加密技术,并分享提高个人和企业安全防护能力的方法,旨在为读者提供一份全面的网络安全与信息保护指南。
190 33
|
JavaScript 前端开发 数据可视化
6 个用于 3D 网页图形渲染的最佳 WebGL 库
现代前端、游戏和Web开发正是WebGL可以转化为数字杰作的东西。使用GPU绘制在浏览器屏幕上生成的矢量元素,WebGL创建交互式Web图形,从而获得用户体验。视觉元素的质量和复杂性使该工具在HTML或CSS等其他方法中脱颖而出。
887 0
|
传感器 数据可视化 JavaScript
状态机(State Machines):理解、设计和应用有限状态机
状态机(State Machines)是一种强大的计算模型和设计工具,用于建模和控制有限状态的系统和行为。无论是在软件开发、自动化控制、游戏设计还是其他领域,状态机都发挥着关键作用。本博客将深入探讨状态机的概念、工作原理以及如何在不同应用中设计和应用它们。
9680 0
|
Rust 并行计算 JavaScript
函数式编程:革命性的编程范式
函数式编程:革命性的编程范式
|
JavaScript 前端开发 API
深入理解vue组件生命周期,你一定要看到最后,最后有深入探讨
深入理解vue组件生命周期,你一定要看到最后,最后有深入探讨
576 0
|
存储 分布式计算 Hadoop
【Hadoop】HDFS 读写流程
【4月更文挑战第9天】【Hadoop】HDFS 读写流程
|
Prometheus 监控 Cloud Native
JVM工作原理与实战(三十三):监控GC过程的工具
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了jstat工具、VisualVM插件、Prometheus + Grafana、GC日志等内容。
528 0
下一篇
oss云网关配置