当前位置: 首页 > news >正文

深入解析 Java 方法引用:Lambda 表达式的进化之路

前言

方法引用是 Java 8 提供的一种新特性,它允许我们更简洁地传递现有方法作为参数。这项特性实际上是对 Lambda 表达式的一种补充,通过方法引用,我们可以直接引用现有方法,而无需编写完整的Lambda表达式。最近在使用方法引用的过程中有了一些感悟,这里希望以文章的形式记录下来,与大家分享。

1. 背景

最近在使用 Mybatis-plus 这个框架,这个框架能在 Mybatis 的基础上减少简单 SQL 的编写,直接使用Java 代码的方式拼接我们想要的 SQL,我为了研究怎样才能不写 SQL 也是在尽量多地翻看 Mybatis-plus 相关文档,尽量能用代码解决地绝不写 SQL。

1.1. LambdaQueryMapper

LambdaQueryMapper 是 MyBatis-Plus 提供的一种基于 Lambda 表达式的查询方式。通过使用 LambdaQueryMapper,我们可以利用 Lambda 表达式来编写类型安全的、更具可读性的查询代码,而无需担心手动编写 SQL 或者处理字符串连接等操作。下面是一个查询示例:

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.additional.query.impl.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.additional.query.impl.LambdaQueryWrapper;public class UserService {private UserMapper userMapper;// 查询用户列表的方法示例public List<User> queryUserList(String username, Integer age, String email) {LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();// 构建查询条件queryWrapper.eq(User::getUsername, username).gt(age != null, User::getAge, age).like(email != null, User::getEmail, email);// 执行查询List<User> userList = userMapper.selectList(queryWrapper);return userList;}
}

在这个查询示例中,我们可以通过 User::getAge 指定 age 字段为查询条件,通过 User::getEmail来指定 email 为查询条件。这种形式的 Java 代码就叫做方法引用。

1.2. 函数式接口

说到方法引用,就不得不说函数式接口。在 java 中只有一个抽象方法的接口叫做函数式接口,每一种方法引用类型实际上对应一个函数式接口。

例如我们比较熟悉的 Runnable ,它其实就是一个函数式接口。它的定义如下:

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

它就代表可以接收一个没有参数也没有返回值的方法:

public class Test {public static void main(String[] args) {Test test = new Test();test.runnable(test::getXxx);}public void runnable(Runnable runnable){runnable.run();}public void getXxx(){System.out.println("run");}
}

同样的,如果我希望接收只有一个参数且只有一个返回值的方法进来,怎么做呢?Java 中也提供了这样的函数式接口 Function<T,R>:

@FunctionalInterface
public interface Function<T, R> {R apply(T t);
}

它接收一个参数 T,且返回一个类型 R,所以我们就可以这么用

public class Test {public static void main(String[] args) {Test test = new Test();test.function(test::getXyy);}public <T,R> void function(Function<T,R> function){}public Integer getXyy(String s){return 1;}
}

1.3. 对于 User::getAge 疑惑

通过上面的叙述,我们知道只要在方法中声明函数式接口,我们就可以使用方法引用的形式将函数作为方法参数传入方法中。但是我在查看 LambdaQueryWrapper 源码的过程中方法,它在 eq、like 等方法中定义的参数类型为函数是接口是 SFunction<T,R>

@FunctionalInterface
public interface SFunction<T, R> extends Function<T, R>, Serializable {
}

也就是说它本质也是一个 Function<T,R> ,匹配的是一个参数以及一个返回值的方法,但是 User::getAge对应的 get 方法只有返回值而没有参数,这是怎么匹配上的?

2. 理解

经过一番查找资料,我终于理解了。下面就由小的来解释一下其中的原由。方法引用我们可以将其分成两部分,以 User::getAge 为例,前半部分是 User,它既可以是 User 类,也可以是 User 对象。后半部分是 getAge 它既可以是类方法(静态方法)也可以是对象方法(非静态方法),理论上有四种组合,即:

  • 对象-类方法
  • 对象-对象方法
  • 类-类方法
  • 类-对象方法

2.1. 对象::类方法

这种形式是不被允许的,会直接报错:

2.2. 对象::对象方法

如果是这种,其实就是直接匹配,也就是说,方法的类型必须和方法引用对应,以 Function<T,R> 为例,必须得是一个参数和一个返回值,代码如下:

public class Test {public static void main(String[] args) {Test test = new Test();//引用为对象test.function(test::getXyy);}public <T,R> void function(Function<T,R> function){}public  Integer getXyy(String s){return 1;}
}

2.3. 类::类方法

这种和上面是一样的,也是要遵循参数和返回值相对应的规则:

public class Test {public static void main(String[] args) {Test test = new Test();//引用变为类test.function(Test::getXyy);}public <T,R> void function(Function<T,R> function){}/*** 变为静态方法*/public static Integer getXyy(String s){return 1;}
}

2.4. 类::对象方法

而对于类::对象方法这种形式,它就有点特殊了,它存在一个隐式转换,我们先看代码:

public class Test {public static void main(String[] args) {Test test = new Test();test.function(Test::getXyy);}public <T,R> void function(Function<T,R> function){}/*** 无参数、一个返回值的对象方法*/public Integer getXyy(){return 1;}
}

function 期待的参数类型是:一个参数、一个返回值的方法,然而对应的却是:无参数、一个返回值的方法,这是怎么回事呢?

原来,对于这种类型的方法引用存在一个隐式转化,即:Test::getXyy 等价于 (Test t) -> t.getXyy(),而 (Test t) -> t.getXyy()代表的就是:一个参数,一个返回值的方法,所以才能匹配 Function<T,R>函数式接口类型。

为什么它需要做这样的转换?本质上是由于类是无法调用对象方法,所以对于 类::对象方法这种形式的方法引用都需要进行这样的隐式转换。

相关文章:

深入解析 Java 方法引用:Lambda 表达式的进化之路

前言 方法引用是 Java 8 提供的一种新特性&#xff0c;它允许我们更简洁地传递现有方法作为参数。这项特性实际上是对 Lambda 表达式的一种补充&#xff0c;通过方法引用&#xff0c;我们可以直接引用现有方法&#xff0c;而无需编写完整的Lambda表达式。最近在使用方法引用的…...

MySQL作业 (3)多表查询

多表查询 1.创建student和score表2.为student表和score表增加记录3.查询student表的所有记录4.查询student表的第2条到4条记录5.从student表查询所有学生的学号&#xff08;id&#xff09;、姓名&#xff08;name&#xff09;和院系&#xff08;department&#xff09;的信息6.…...

ConcurrentHashMap和HashMap的区别

什么是HashMap &#xff08;1&#xff09;HashMap 是基于 Map 接口的非同步实现&#xff0c;线程不安全&#xff0c;是为了快速存取而设计的&#xff1b;它采用 key-value 键值对的形式存放元素&#xff08;并封装成 Node 对象&#xff09;&#xff0c;允许使用 null 键和 nul…...

MCM备赛笔记——图论模型

Key Concept 图论是数学的一个分支&#xff0c;专注于研究图的性质和图之间的关系。在图论中&#xff0c;图是由顶点&#xff08;或节点&#xff09;以及连接这些顶点的边&#xff08;或弧&#xff09;组成的。图论的模型广泛应用于计算机科学、通信网络、社会网络、生物信息学…...

算法笔记(动态规划入门题)

1.找零钱 int coinChange(int* coins, int coinsSize, int amount) {int dp[amount 1];memset(dp,-1,sizeof(dp));dp[0] 0;for (int i 1; i < amount; i)for (int j 0; j < coinsSize; j)if (coins[j] < i && dp[i - coins[j]] ! -1)if (dp[i] -1 || dp[…...

开发实践_阶段三

编写一个告知APP。 需求&#xff1a; 1.登录、注册 2.发布定向讯息&#xff1a;检测是否登录&#xff0c;是则向用户或用户组发布 ”名称 时间“ &#xff1b;否则提示登录 3.讯息接收&#xff1a;检测是否登录&#xff0c;是则查看收到信息&#xff08;未读数&#xff09…...

codegeex和通义灵码辅助编程——以及通义灵码无法登陆的bug解决

通义的速度更快&#xff0c;延迟低&#xff0c;150ms。 codegeex速度慢些&#xff0c;延迟较高&#xff0c;500ms。 个人评价&#xff1a;延迟低的会很好地改善使用体验&#xff0c;所以通义加分。 但是整体功能上还是codegeex强一些&#xff0c;可以选中代码进行对话&#xf…...

Android14之DefaultKeyedVector实现(一百八十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…...

银河麒麟操作系统 v10 中离线安装 Docker

银河麒麟操作系统 v10 中离线安装 Docker 1. 查看系统版本2. 查看 Linux 内核版本&#xff08;3.10以上&#xff09;3. 查看 iptabls 版本&#xff08;1.4以上&#xff09;4. 判断处理器架构5. 离线下载 Docker 安装包6. 移动解压出来的二进制文件到 /usr/bin 目录中7. 配置 Do…...

如何系统的学习Python

学习 Python 的时候&#xff0c;可以按照以下步骤进行系统学习&#xff1a; 学习 Python 基础知识&#xff1a;首先了解 Python 的基础语法、数据类型、变量和运算符等基本概念。可以通过阅读《Python编程从入门到实践》等经典教材来建立基础。也可以通过翻阅Python官方文档来进…...

Java并发基础:一文讲清util.concurrent包的作用

java.util.concurrent包是 Java 中用于并发编程的重要工具集&#xff0c;提供了线程池、原子变量、并发集合、同步工具类、阻塞队列等一系列高级并发工具类&#xff0c;使用这些工具类可以极大地简化并发编程的难度&#xff0c;减少出错的可能性&#xff0c;提高程序的效率和可…...

C++PythonC# 三语言OpenCV从零开发(2):教程选择

文章目录 相关专栏前言视频教学和官方文档视频教程OpenCV 官方教程最终选择我的最终选择 相关专栏 C&Python&Csharp in OpenCV 前言 OpenCV 有官方的教程和简单的视频教程&#xff1a; OpenCV 官方教程 B站也有相关的视频教学 OpenCV4 C 快速入门视频30讲 - 系列合集 …...

【嘉立创EDA-PCB设计指南】3.网络表概念解读+板框绘制

前言&#xff1a;本文对网络表概念解读板框绘制&#xff08;确定PCB板子轮廓&#xff09; 网络表概念解读 在本专栏的上一篇文章【嘉立创EDA-PCB设计指南】2&#xff0c;将设计的原理图转为了PCB&#xff0c;在PCB界面下出现了所有的封装&#xff0c;以及所有的飞线属性&…...

nodejs前端项目的CI/CD实现(二)jenkins的容器化部署

一、背景 docker安装jenkins&#xff0c;可能你会反问&#xff0c;这太简单了&#xff0c;有什么好讲的。 我最近就接手了一个打包项目&#xff0c;它是一个nodejs的前端项目&#xff0c;jenkins已在容器里部署且运行OK。 但是&#xff0c;前端组很追求新技术&#xff0c;不…...

python爬虫案例分享

当然&#xff0c;我可以分享一个基本的Python爬虫示例。这个示例将使用Python的requests库来抓取网页内容&#xff0c;然后使用BeautifulSoup库来解析和提取信息。我们将构建一个简单的爬虫来从一个示例网站抓取标题。 Python爬虫示例 目标 提取某网站的标题。 需要的库 r…...

【CC++】为什么 scanf 函数在读取字符串时不需要用取地址运算符

在C语言中如何使用 scanf 读取字符串 在C语言中&#xff0c;字符串实际上是字符数组&#xff0c;所以我们可以使用scanf函数来读取字符串。但是&#xff0c;需要注意的是&#xff0c;scanf在读取字符串时会在遇到空格、制表符或换行符时停止。因此&#xff0c;它不能用于读取包…...

Linux dirs命令教程:dirs命令详解与实例(附实例详解和注意事项)

Linux dirs命令介绍 dirs这是一个内置在shell中的命令&#xff0c;用于显示当前被记忆的目录列表。默认状态下&#xff0c;它会按照stack的方式储存目录&#xff0c;即最后加入的目录会被首先列出来。 Linux dirs命令适用的Linux版本 dirs命令在所有常见的Linux发行版中都适…...

掌握虚拟化:PVE平台安装教程与技术解析

&#x1f31f;&#x1f30c; 欢迎来到知识与创意的殿堂 — 远见阁小民的世界&#xff01;&#x1f680; &#x1f31f;&#x1f9ed; 在这里&#xff0c;我们一起探索技术的奥秘&#xff0c;一起在知识的海洋中遨游。 &#x1f31f;&#x1f9ed; 在这里&#xff0c;每个错误都…...

Godot FileDialog无法访问其它盘符的文件

问题描述 使用Godot的FileDialog对象访问Windows系统的文件&#xff0c;例如&#xff1a; func _on_hud_sig_save():var dlg FileDialog.new()dlg.set_access(FileDialog.ACCESS_FILESYSTEM)dlg.set_file_mode(FileDialog.FILE_MODE_SAVE_FILE)add_child(dlg)dlg.popup_cent…...

TestNG注释

目录 TestNG注释列表 BeforeXXX和AfterXXX注释放在超类上时如何工作&#xff1f; 使用BeforeXXX和AfterXXX TestNG注释 TestNG是一个测试框架&#xff0c;旨在简化广泛的测试需求&#xff0c;从单元测试&#xff08;隔离测试一个类&#xff09;到集成测试&#xff08;测试由…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...

QT开发技术【ffmpeg + QAudioOutput】音乐播放器

一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下&#xff0c;音视频内容犹如璀璨繁星&#xff0c;点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频&#xff0c;到在线课堂中知识渊博的专家授课&#xff0c;再到影视平台上扣人心弦的高清大片&#xff0c;音…...

麒麟系统使用-进行.NET开发

文章目录 前言一、搭建dotnet环境1.获取相关资源2.配置dotnet 二、使用dotnet三、其他说明总结 前言 麒麟系统的内核是基于linux的&#xff0c;如果需要进行.NET开发&#xff0c;则需要安装特定的应用。由于NET Framework 是仅适用于 Windows 版本的 .NET&#xff0c;所以要进…...

CppCon 2015 学习:Reactive Stream Processing in Industrial IoT using DDS and Rx

“Reactive Stream Processing in Industrial IoT using DDS and Rx” 是指在工业物联网&#xff08;IIoT&#xff09;场景中&#xff0c;结合 DDS&#xff08;Data Distribution Service&#xff09; 和 Rx&#xff08;Reactive Extensions&#xff09; 技术&#xff0c;实现 …...