Java 函数式编程 详细介绍

news2025/1/23 9:08:25

在兼顾面向对象特性的基础上,Java语言通过Lambda表达式与方法引用等,为开发者打开了函数式编程的大门。 下面我们做一个初探。

Lambda的延迟执行

有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以 作为解决方案,提升性能。

性能浪费的日志案例

 
注:日志可以帮助我们快速的定位问题,记录程序运行过程中的情况,以便项目的监控和优化。

一种典型的场景就是对参数进行有条件使用,例如对日志消息进行拼接后,在满足条件的情况下进行打印输出:

 
public class Demo01Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(1, msgA + msgB + msgC);
}
private static void log(int level, String mgs) {
if (level == 1) {
System.out.println(mgs);
}
}
}

这段代码存在问题:无论级别 level 是否满足要求,作为 log 方法的第二个参数,三个字符串一定会首先被拼接并传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费。

 
备注:SLF4J是应用非常广泛的日志框架,它在记录日志时为了解决这种性能浪费的问题,并不推荐首先进行 字符串的拼接,而是将字符串的若干部分作
为可变参数传入方法中,仅在日志级别满足要求的情况下才会进 行字符串拼接。例如: LOGGER.debug("变量{}的取值为{}。", "os", "macOS") ,
其中的大括号 {} 为占位 符。如果满足日志级别要求,则会将“os”和“macOS”两个字符串依次拼接到大括号的位置;否则不会进行字 符串拼接。这也是
一种可行解决方案,但Lambda可以做到更好。

体验Lambda的更优写法

使用Lambda必然需要一个函数式接口:

 
@FunctionalInterface
public interface MessageBuilder {
/**
* 信息生成器
* @return 生成的信息
*/
public abstract String builderMessage();
}

然后对 log 方法进行改造:

 
public class Demo02Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(1, () -> msgA + msgB + msgC);
}
private static void log(int level, MessageBuilder mb) {
if (level == 1) {
System.out.println(mb.builderMessage());
}
}
}

改造前后的对比:

这样一来,只有当级别满足要求的时候,才会进行三个字符串的拼接;否则三个字符串将不会进行拼接。

证明Lambda的延迟

下面的代码可以通过结果进行验证:

 
public class Demo03Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(2, () -> {
System.out.println("Lambada 执行!");
return msgA + msgB + msgC;
});
}
private static void log(int level, MessageBuilder mb) {
if (level == 1) {
System.out.println(mb.builderMessage());
}
}
}

这里只是在调用 log 方法的时候,将传入的Lambda稍作修改,

当传入的 level = 1 的时候,控制台输出:

 
Lambada 执行!
Hello World Java

当传入的 level != 1 的时候,控制台没有输出。

从结果中可以看出,在不符合级别要求的情况下,Lambda将不会执行。从而达到节省性能的效果。

 
扩展:实际上使用内部类也可以达到同样的效果,只是将代码操作延迟到了另外一个对象当中通过调用方法来完成。而是否调
用其所在方法是在条件判断之后才执行的。
public class Demo04Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(1, new MessageBuilder() {
@Override
public String builderMessage() {
System.out.println("Lambada 执行!");
return msgA + msgB + msgC;
}
});
}
private static void log(int level, MessageBuilder mb) {
if (level == 1) {
System.out.println(mb.builderMessage());
}
}
}

使用Lambda作为参数和返回值

如果抛开实现原理不说,Java中的Lambda表达式可以被当作是匿名内部类的替代品。如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式 接口作为方法参数。

Lambda作为参数

例如 java.lang.Runnable 接口就是一个函数式接口,假设有一个 startThread 方法使用该接口作为参数,那么就可以使用Lambda进行传参。这种情况其实和 Thread 类的构造方法参数为 Runnable 没有本质区别。

匿名内部类作为参数,创建新的线程并执行:

 
public class Demo01Runnable {
public static void main(String[] args) {
startThread(new Runnable() {
@Override
public void run() {
System.out.println("线程任务执行!");
}
});
}
/**
* 创建一个新的线程,赋予任务,然后开启线程
* @param runnable 传入Thread类的接口,实现创建新线程
*/
private static void startThread(Runnable runnable) {
new Thread(runnable).start();
}
}

运行程序,控制台输出:

 
线程任务执行!

Lambda作为参数,创建新的线程并执行:

 
public class Demo02Runnable {
public static void main(String[] args) {
startThread(
() -> System.out.println("线程任务执行!")
);
}
/**
* 创建一个新的线程,赋予任务,然后开启线程
* @param runnable 传入Thread类的接口,实现创建新线程
*/
private static void startThread(Runnable runnable) {
new Thread(runnable).start();
}
}

运行程序,控制台输出:

 
线程任务执行!

Lambda作为返回值

类似地,如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。当需要通过一个方法来获取一个 java.util.Comparator 接口类型的对象作为排序器时,就可以调该方法获取。

Lambda作为返回值,字符串的长短比较:

 
import java.util.Arrays;
import java.util.Comparator;
public class DemoComparator {
public static void main(String[] args) {
String[] array = { "abc", "ab", "a" };
System.out.println("使用比较器比较之前:" + Arrays.toString(array));
Arrays.sort(array, newComparator());
System.out.println("使用比较器比较之后:" + Arrays.toString(array));
}
/**
* 字符串a、b的长短比较,自己定义比较器规则,生序排序,字符串长的排在后面。
* @return 布尔值,
* a.length() - b.length() < 0 返回 false,
* a.length() - b.length() > 0 返回 true,
* a.length() = b.length() 返回 0
*/
public static Comparator<String> newComparator() {
return (a, b) -> a.length() - b.length();
}
}

匿名内部类作为返回值,字符串的长短比较:

 
import java.util.Arrays;
import java.util.Comparator;
public class DemoComparator {
public static void main(String[] args) {
String[] array = { "abc", "ab", "a" };
System.out.println("使用比较器比较之前:" + Arrays.toString(array));
Arrays.sort(array, newComparator());
System.out.println("使用比较器比较之后:" + Arrays.toString(array));
}
public static Comparator<String> newComparator1() {
return new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
};
}
}

运行程序,控制台输出一样:

 
使用比较器比较之前:[abc, ab, a]
使用比较器比较之后:[a, ab, abc]

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/521889.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PID现场参数调试解密

看图 反馈与给定曲线 1.超调过大&#xff0c;减小比例&#xff0c;增大积分时间 2.迅速变化&#xff0c;存在小超调 3.实际值缓慢接近设定值&#xff0c;并且无超调的达到设定值 4.增益系数太小和/或微分时间太长 5.益系数太小和/或积分时间太长 公式(西门子FB41) 举例 现象…

20230513查找瑞芯微RK3588开发板以及对DP接口的支持

20230513查找瑞芯微RK3588开发板以及对DP接口的支持 2023/5/13 17:43 01、t-firefly https://www.t-firefly.com/ https://www.t-firefly.com/product/industry/aio1684xjd4 https://www.t-firefly.com/product/industry/aio3588q https://item.taobao.com/item.htm?spma1z10.…

三轴和直剪

目录 常规三轴 成样 预压 围压加载 二维直剪 成样 预压 围压加载 常规三轴 成样 cylinder keyword ...(3D ONLY) Generate a cylinder in 3D. If the name keyword has not been specified, then s cylinderWall. By default, the cylinder is closed and each side o…

HTTP协议(二)/HTTPS

HTTPS是啥 https是在http协议的基础上&#xff0c;加上了数据的加密解密层&#xff0c;即TLS/SSL。在进行http请求的时候&#xff0c;通过TLS/SSL进行加密&#xff0c;在响应的时候&#xff0c;也会通过TLS/SSL进行解密。加密解密层TLS/SSL不会对下三层加密解密&#xff0c;而…

【27】核心易中期刊推荐——计算机工程与技术

🚀🚀🚀NEW!!!核心易中期刊推荐栏目来啦 ~ 📚🍀 核心期刊在国内的应用范围非常广,核心期刊发表论文是国内很多作者晋升的硬性要求,并且在国内属于顶尖论文发表,具有很高的学术价值。在中文核心目录体系中,权威代表有CSSCI、CSCD和北大核心。其中,中文期刊的数…

[ 云计算 华为云 ] 华为云开天 aPaaS:构建高效的企业数字化平台(下)

文章目录 前言四、华为云开天aPaaS 核心功能4.1 业务模型管理4.2 连接器4.2.1 连接器的种类4.2.1.1 公共连接器4.2.1.2 私有连接器 4.2.2连接器的开发步骤 4.3 自动化流4.3.1 自动化流介绍4.3.2 自动化流日志监控 4.4 自定义逻辑处理4.5 分享连接器和流模板 五、aPaaS 的应用实…

关于C/C++语言重复包含头文件,编译时报错已定义的宏未定义的原因及解决方法

在编写一个文件较多的单片机程序时&#xff0c;为了在一个文件中定义的变量或宏能被另一个文件使用&#xff0c;经常会写成在多个头文件相互包含&#xff0c;由此将可能会导致明明已经定义的宏&#xff0c;且已经将宏所在的文件使用 #include 包含&#xff0c;编译时仍会报错未…

自学大语言模型的应用程序框架Langchain(初入门)

现阶段chatGPT非常火热。带动了第三方开源库&#xff1a;LangChain火热。它是一个在大语言模型基础上实现联网搜索并给出回答、总结 PDF 文档、基于某个 Youtube 视频进行问答等等的功能的应用程序。 什么是Langchain LangChain 是一个用于开发由语言模型驱动的应用程序的框架…

Vue 3.3 浪客剑心

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: 三十天精通 Vue 3 文章目录 特征弃用一、 < s c r i p t s e t u p > <script setup…

【啃书C++Primer5】-编写一个简单C++程序

每个C程序都包含一个或多个函数(function)&#xff0c;其中一个必须命名为main。操作系统通过调用main来运行C程序。下面是一个非常简单的main函数&#xff0c;它什么也不干&#xff0c;只是返回给操作系统一个值: int main() {return 0; }一个函数的定义包含四部分:返回类型(r…

【讨论话题】 工作中应不应该出于好心,帮助同事完成不属于你职责范围内的问题?

前言 职场如战场&#xff0c;职场中经常有一种好心帮忙反而成了义务的乱相存在。我们要做一个好人&#xff0c;同事要防备一些坏人。 勿以善小而不为&#xff0c;勿以恶小而为之 文章目录 前言社群内部讨论社群内部讨论总结师父的回答如何优雅的说不总结 社群内部讨论 先来看…

声音合成——Foley Sound——DECASE项目——多模态智能感知与应用——代码实现(6)

文章目录 概述encoder的编写过程代码编写运行结果问题总结 decoder的编写过程知识补充关于逆卷积 代码编写运行结果总结 Autoencoder模型编写 compile方法 train方法 保存和加载模型模块编写实现代码——autoencoder代码实现代码——train代码实现代码——保存和加载模型的代…

GPT-4最强竞品迎来重磅升级:一次10万token,百页资料一分钟总结完毕

夕小瑶科技说 分享 来源 | 量子位 作者 | 丰色 萧箫 号称ChatGPT“最强竞争对手”的Claude&#xff0c;今天迎来史诗级更新—— 模型记忆力原地起飞&#xff0c;现在1分钟看完一本数万字的小说&#xff0c;已经不在话下。 消息一出&#xff0c;评论区直接炸了&#xff0c;网友…

HTTP第九讲——你能写出正确的网址吗?

应该用什么来标记服务器上的资源呢&#xff1f;怎么区分“这个”资源和“那个”资源呢&#xff1f; 用的是 URI&#xff0c;也就是统一资源标识符&#xff08;Uniform Resource Identifier&#xff09;。因为它经常出现在浏览器的地址栏里&#xff0c;所以俗称为“网络地址”&a…

猫妹人生中的第一张Python证书长这样(1)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 欢迎和猫妹一起&#xff0c;快乐学习&#xff0c;茁壮成长&#xff01; 猫妹和她的朋友们 猫妹和她的朋友们有两层含义。 一是新的合集名称&#xff0c;在这个合集里面&am…

redis从零开始(3)----基本类型hyperloglog/geo/stream

接上文 redis基本类型 HyperLogLog 简介 HyperLogLog是用于「统计基数」的数据集合类型&#xff0c;基数统计就是指统计一个集合中不重复的元素个数&#xff0c; 但是准确率不是百分百&#xff0c;即他可以提供不精确的去重计数。HyperLogLog 的优点是&#xff0c;在输入元素…

.Net平台下OpenGL绘制图形(1)(VS2019,Winform,C#)

1、介绍 OpenGL&#xff08;英语&#xff1a;Open Graphics Library&#xff0c;译名&#xff1a;开放图形库或者“开放式图形库”&#xff09;是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口&#xff08;API&#xff09;。这个接口由近350个不同的函数调用组成…

【图解KMP算法】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 找出字符串中第一个匹配项的下标 1.什么是KMP…

Windows 下 VMware 虚拟机的 vmdk 文件的压缩

在 Windows 下玩虚拟机&#xff0c;一般大家都会安装 VMware 软件&#xff0c;然后通过软件安装其它操作系统环境&#xff08;如 Linux&#xff09;来模拟各个服务器环境。然而大家在使用过程中可以发现这样一个问题&#xff0c;那就是每个操作系统对应主机磁盘目录中&#xff…

与小我11岁的人一次PK

基本情况 时间过得很快&#xff0c;如同流水一般。 我的同事F已经离职一段时间了&#xff0c;怎么说呢&#xff1f; 公司领导的想让我们进行PK,谁是胜利者&#xff0c;谁留下&#xff0c;这的确与别的公司不同&#xff0c;别的公司通过面试&#xff0c;公司领导感觉那样不足体…