Java Lambda和方法引用

一、Lambda

Lambda表达式,即函数式编程或者匿名函数,Java是从Java8开始支持的,这个概念并不是Java特有的,很多语言(比如JS)都有这个概念,它允许将一系列行为封装后作为参数传递,并可以将其执行一次或多次。便于写出更简洁、灵活、紧凑的代码。

1.Lambda格式

Lambda表达式的基本结构:[函数入参] -> [函数体]
注意:
->” 是一个整体,中间不能有空格;
函数入参中的参数无需指定参数类型,只有参数名,类型由编译器自动推断;
函数体只有一条语句时可以简写(省略大括号、分号和return)

  • 左侧无入参
() -> { /*...*/ }
  • 左侧只有一个入参,有两种写法
(a) -> { /*...*/ }
//或者简写为下面的形式(省略圆括号)
a -> { /*...*/ }
  • 左侧有多个入参,圆括号不能省略
(a,b) -> { /*...*/ }
  • 右侧函数体只有一条语句
//---------------------无返回-------------------
() -> { System.out.println("hello");
}
//可以简写为下面的形式(去掉大括号和语句结尾的分号)
() -> System.out.println("hello")//---------------------带返回-------------------
a -> {return a+1;
}
//可以简写成下面的形式(除了去掉大括号和语句结尾的分号, return 关键字也省略了)
a -> a+1
  • 右侧函数体有多条语句
a -> { //函数体的大括号不能省略,分号、return关键字也不能省略int b = 2;return a+b;
}
2.Lambda在Java中的本质

先看看在Lambda出现之前在Java中如何定义一个匿名函数

new Thread(new Runnable() { //这是一个匿名内部类@Overridepublic void run() {//方法实现System.out.println("hello");}
}).start();

Lambda的写法

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

可以看出Lambda的本质是 匿名内部类
Lambda看似实现了一个方法,实则实现了一个接口
Lambda的定义形式和run方法是一一对应的,run方法没有入参所以lambda的入参是"()",run方法没有返回,所以lambda也无需return。

3.什么样的匿名内部类可以用Lambda替代?

让我们先来看看Runnable接口的源码

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

试想一下Runnable内部定义了多个方法,Runnable的匿名内部类还能用Lambda代替吗?
答案是不可以,因为有多个方法的时候,lambda就不知道和哪一个方法对应了,也无法做类型推断

  • 结论

只有定义了一个方法(default方法除外)的接口的匿名内部类都可以简写成Lambda的形式
也就是函数式接口都可以用lambda作为实现

  • 其他说明

1.接口中的default方法是为了兼容老接口使用lambda的一种措施,default关键字声明的方法可以在接口中定义好方法的实现
2.@FunctionalInterface是一个声明注解,声明该接口是一个函数式接口(支持Lambda),但是本身不影响Lambda的支持(也就是这个注解可以省略)
3.虽然@FunctionalInterface注解不是必须的,但还是强烈建议添加上这个注解,不仅仅因为这是一个基本的惯例,而是加上这个注解后,编译器会帮我们检查这个接口是不是函数式接口,如果不是就会报编译错误,如下:
在这里插入图片描述

4.JDK内置的Lambda常用范式

java.util.function下面定义了一些常用的Lambda范式,为了使这些范式具有通用性,他们都定义成了泛型类

  • 一个入参有返回:Function<T,R>
@FunctionalInterface
public interface Function<T, R> {//只有一个入参(类型为T),有返回(类型为R)R apply(T t);//省略其他方法...
}
  • 一个入参无返回:Consumer<T>
@FunctionalInterface
public interface Consumer<T> {//只有一个入参(类型是T),无返回void accept(T t);//这就是一个写在接口里的default方法default Consumer<T> andThen(Consumer<? super T> after) {Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };}
}
  • 无入参有返回:Supplier<T>
@FunctionalInterface
public interface Supplier<T> {//无入参,有返回(类型为T)T get();
}
  • 一个入参返回布尔:Predicate<T>
@FunctionalInterface
public interface Predicate<T> {//只有一个入参(类型为T),有返回(类型为布尔)boolean test(T t);//省略其他方法...
}

Predicate<T> 可以看作是 Function<T,Boolean>

以上的四个范式除了Supplier其他三个还有两个入参的版本,
分别是BiFunction<T, U, R>BiConsumer<T, U>BiPredicate<T, U>

5.天然的策略模式

Lambda是天然的策略模式,当我们在某个地方想要一个结果,但是获得结果的具体实现未知或者有多种的时候就可以考虑用函数式接口作为入参让调用方去实现,这种设计在集合流Stream框架中尤为多见。

public static String doSomething(String name, Function<String, String> f){return f.apply(name); //某个人做一些事(什么事不知道,调用的时候再定义)
}
public static void main(String[] args) {System.out.println(doSomething("张三", name -> name + "吃香蕉")); //张三吃香蕉System.out.println(doSomething("张三", name -> name + "吃苹果")); //张三吃苹果
}

一般来说JDK内置的Lambda函数式接口(Function、Consumer、Supplier…)能满足大部分的场景,如果找不到满足需要的范式,可以自己定义函数式接口(定义只有一个方法的接口,加上@FunctionalInterface注解但不是必须)

6.柯里化

柯里化即把一部分变量固化在函数内部,本质就是闭包持有的外部变量
详情见我的另一篇博客: Java柯里化实现

7.Lambda引用的外部变量不可变

在Java中,Lambda表达式内部访问的局部变量必须是final或者事实上的final。这是因为Lambda表达式实际上是一个闭包,它包含了对其外部的变量的引用,而这些变量在Lambda表达式执行期间不能被修改,否则会导致线程安全问题。

public static void main(String[] args) {int count = 0;Runnable r = () -> {count++; // 编译错误,变量count必须被声明为final或者事实上的final};r.run();
}
  • 可修改为:
public static void main(String[] args) {AtomicInteger count = new AtomicInteger(0);Runnable r = () -> {count.incrementAndGet(); // 使用AtomicInteger代替int,避免线程安全问题};r.run();System.out.println("count: " + count.get());
}

二、方法引用

方法引用(Method Reference)是Java 8引入的一项重要特性,它提供了一种简洁、可读性高的方式来直接引用已经存在的方法。

简单的说就是在已存在的方法中(静态方法、对象方法或者构造方法),如果一个方法符合某个Lambda范式,那么就可以用这个方法的引用直接替代Lambda表达式。

1.方法引用格式

方法引用的基本形式:[方法归属]::[方法名]
注意:
::” 是一个整体,中间不能有空格;
方法归属可以是类名、对象名或"this"
在引用构造方法时,方法名为"new"

  • 引用静态方法

ClassName::methodName

  • 引用构造方法

ClassName::new

  • 引用对象方法

objectName::methodName

  • 在当前类引用对象方法

this::methodName

2.代码示例
package com.vz.demo.test;import java.util.function.Function;
import java.util.function.Supplier;public class MethodRefTest {public static String eating(String name, Function<String, String> f){return f.apply(name);}public static String test1(){//return eating("张三", name -> name + "吃香蕉");return eating("张三", MethodRefTest::eatBanana); //引用静态方法}public String test2(){//return eating("李四", name -> name + "吃苹果");return eating("李四", this::eatApple); //引用当前对象方法}public static String eatBanana(String name){return name + "吃香蕉";}public String eatApple(String name){return name + "吃苹果";}public String eatOrange(String name){return name + "吃橙子";}public static MethodRefTest getInstance(Supplier<MethodRefTest> supplier){return supplier.get();}public static void main(String[] args) {System.out.println(test1());//MethodRefTest methodRefTest = getInstance(() -> new MethodRefTest());MethodRefTest methodRefTest = getInstance(MethodRefTest::new); //引用无参构造方法System.out.println(methodRefTest.test2());//System.out.println(eating("王五", name -> name + "吃橙子"));System.out.println(eating("王五", methodRefTest::eatOrange));//引用对象方法}
}
  • 打印结果:
张三吃香蕉
李四吃苹果
王五吃橙子

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JOSEF约瑟 JY8-32B无辅源静态电压继电器整定范围15-130VAC过电压

JY-11电压继电器&#xff1b;JY-11A电压继电器&#xff1b; JY-11B电压继电器&#xff1b;JY-12电压继电器&#xff1b; JY-12A电压继电器&#xff1b;JY-12B电压继电器&#xff1b; JY-21电压继电器&#xff1b;JY-21A电压继电器&#xff1b; JY-21B电压继电器&#xff1b…

[MYSQL数据库]--表内操作(CURD)

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、表的 Cre…

「哈哥赠书活动 - 50期」-『AI赋能写作:AI大模型高效写作一本通』

⭐️ 赠书 - 《AI赋能写作&#xff1a;AI大模型高效写作一本通》 ⭐️ 内容简介 本书以ChatGPT为科技行业带来的颠覆性革新为起点&#xff0c;深入探讨了人工智能大模型如何为我们的创作提供强大支持。本书旨在帮助创作者更好地理解AI的价值&#xff0c;并充分利用其能力提升写…

ARM 汇编指令:(五)CMP指令

目录 1.CMP比较指令 2.指令条件码 cond 1.CMP比较指令 CMP指令是计算机指令集中的一种比较指令&#xff0c;用于比较两个操作数的大小关系或相等性&#xff0c;并根据比较结果设置或更新条件码寄存器&#xff08;或程序状态字&#xff09;的标志位。 指令格式&#xff1a;C…

Redis实现计数统计

介绍 计数器大量应用于互联网上大大小小的项目&#xff0c;你可以在很多场景都能找到计数器的应用范畴&#xff0c;单纯以技术派项目为例&#xff0c;也有相当多的地方会有计数相关的诉求&#xff0c;比如 文章带赞数 收藏数 评论数 用户粉丝数 ...... 技术派中有两种查询…

收藏贴!6个谈薪小技巧,助你拿到满意薪资

Salesforce的就业市场一直在迅猛发展&#xff0c;对Salesforce专业人士的需求持续不断&#xff0c;对优秀人才的需求更大。 本篇文章总结了6个谈薪小技巧&#xff0c;可以帮助SF从业者、求职者拿到满意的薪资。 01 了解市场价格 首先&#xff0c;需要了解当前就业市场的情况…

WPF监控平台(科技大屏)[一]

跟着B站的视频敲了一个略微复杂的WPF界面,链接如下.在这里我详细的写一份博客进行设计总结. 系统介绍和配置及主窗口设计_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Wy421Y7QD?p1&vd_source4796b18a2e4c1ec8a310391a5644b6da 成果展示 实现过程 总体来说,我的…

使用 pg_profile 在 Postgres 中生成性能分析报告

前言&#xff1a; postgres数据库中拥有大量的辅助插件用于帮助DBA更好的分析数据库性能或整个集群&#xff0c;包括索引、I/O、CPU和内存等&#xff0c;pg_profile是基于PostgreSQL标准统计信息视图的诊断工具&#xff0c;它类似于Oracle AWR架构&#xff0c;和Oracle一样&am…

UL1642标准_锂聚合物电池亚马逊测试报告

UL1642标准_锂聚合物电池亚马逊测试报告 什么是锂聚合物电池UL1642标准&#xff1f; UL1642 认证要求涵盖旨在用于技术人员可更换或用户可更换应用的锂离子电池。UL1642 认证要求是为了避免锂离子电池在产品中工作时发生火灾或爆炸的风险。 锂聚合物电池 UL是Underwriters L…

《ElementPlus 与 ElementUI 差异集合》icon 图标使用(包含:el-button,el-input和el-dropdown 差异对比)

安装 注意 ElementPlus 的 Icon 图标 要额外安装插件 element-plus/icons-vue. npm install element-plus/icons-vue注册 全局注册 定义一个文件 element-icon.js &#xff0c;注意代码第 6 行。加上了前缀 ElIcon &#xff0c;避免组件命名重复&#xff0c;且易于理解为 e…

人工智能迷惑行为大赏!

目录 人工智能迷惑行为大赏 一&#xff1a;人工智能的“幽默”瞬间 1. 图像识别出现AI的极限 2. 小批量梯度下降优化器 3. 智能聊天机器人的冰雹问题 4. 大语言模型-3经典语录 二&#xff1a;技术原理探究 1. 深度学习 2. 机器学习 3. 自然语言处理 4. 计算机视觉 三…

Sui与数据平台ZettaBlock达成合作,为其公测提供数据

Sui一向以闪电般的速度、无限水平扩展著称&#xff0c;现已迅速成为DeFi活动的重要场所。近期&#xff0c;数据平台ZettaBlock宣布在其开创性的Web3数据平台发布中&#xff0c;选择Sui作为基础集成合作伙伴之一。在ZettaBlock的开放测试版发布之际&#xff0c;构建者和开发者将…