Kotlin高阶函数与Lambda表达式及内联函数的介绍

2024-09-07 22:04

本文主要是介绍Kotlin高阶函数与Lambda表达式及内联函数的介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 1、高阶函数
    • 1.1、什么是高阶函数?
      • 1.1.1、不带返回值的高阶函数
      • 1.1.2、带参数且带返回值的高阶函数
      • 1.1.3、与一般的函数进行比较
    • 1.2、如何使用?
    • 1.3、高阶函数有什么作用?
  • 2、Lambda表达式
    • 2.1、什么是Lambda表达式?
      • 2.1.1、无参数的写法
      • 2.1.2、有参数的写法
      • 2.1.3、有参数且有返回值的写法
    • 2.2、如何使用?
    • 2.3、Lambda表达式有什么作用?
  • 3、内联函数
    • 3.1、什么是内联函数?
      • 3.1.1、inline内联函数的写法
      • 3.1.2、noinline禁止内联的写法
      • 3.1.3、crossinline限制Lambda表达式直接调用 return的写法
    • 3.3、如何使用?
    • 3.2、内联函数有什么作用?
      • 3.3.1、高阶函数为什么会造成性能损耗?
      • 3.3.2、使用内联函数为什么可以解决高阶函数性能损耗问题?
  • 4、总结
  • 5、参考链接

1、高阶函数

1.1、什么是高阶函数?

高阶函数就是将函数类型用作参数或返回值的函数。

举例说明,看看高阶函数到底长什么样子。

1.1.1、不带返回值的高阶函数

例子如下:

    private fun testA(fun1: (String) -> Unit): String {return fun1("这是testA中")}

这就是一个Kotlin的高阶函数的写法,注意看fun1就是我们传递的函数类型的参数。

-> String:代表的就是返回值的数据类型,也可以是Int、、Long等等;
-> Unit:代表的就是不带返回值的写法,用Unit固定表示类似java的viod用法。

1.1.2、带参数且带返回值的高阶函数

例子如下:

    private fun testA(fun1: (String) -> String): String {return fun1("这是testA中")}

1.1.3、与一般的函数进行比较

一般的函数带参数的写法,如下:

	fun test(arg:String):String{retrun "返回值:$arg"}

argfun1都是参数,kotlin的高级之处就在于,可以直接把一个函数当作形式参数(又称函数类型的参数)来用。

1.2、如何使用?

使用也不复杂,举个例子:

	override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val res = testA(::testFun)println(res);//输出:返回值:这是在testA中}private fun testA(fun1: (String) -> String): String {val fun1Res=  fun1("这是在testA中")return fun1Res;}fun testFun(arg:String):String{retrun "返回值:$arg"}

1.3、高阶函数有什么作用?

可以提高代码的灵活性,以前只能传对象类型的参数或者基本类型的参数,现在可以把一个函数当作参数进行传递。

2、Lambda表达式

2.1、什么是Lambda表达式?

最简单的理解就是写在大括号里面的代码段,与匿名内部类写法非常类似。

举几个Lambda表达式的例子,看看Lambda表达式到底长什么样子:

2.1.1、无参数的写法

举个例子,如下:

	{println("Lambda表达式无参数写法")}

2.1.2、有参数的写法

举个例子,如下:

 	val res3: (String, String) -> Unit = { arg: String, arg1: String ->println("Lambda表达式有参数写法$arg , $arg1 ")} 

参数的类型可以自动推断,上面这段代码可简写为:

	val res3 = { arg: String, arg1: String ->println("Lambda表达式有参数写法$arg , $arg1 ")}

2.1.3、有参数且有返回值的写法

举个例子,如下:

    val res: (String, String) -> String = { arg: String, arg1: String ->arg + "," + arg1 //直接返回值,不需要return关键字}

返回值和参数的类型可以自动推断,上面这段代码可简写为:

	val res = { arg: String, arg1: String ->arg + "," + arg1 //直接返回值,不需要return关键字} 

有返回值的时候,不需要添加return 关键字,否则会提示报错如下:

	'return' is not allowed here

2.2、如何使用?

使用也不复杂,举例如下:

override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)testA {println("Lambda表达式无参数写法")}testB { arg: String ->println("Lambda表达式有参数写法:$arg")}testC { arg: String ->println("Lambda表达式有参数且有返回值的写法:$arg")val resArg = "返回的,$arg"resArg}      }//高阶函数private fun testA(fun1: () -> Unit) {fun1()}//高阶函数private fun testB(fun1: (String) -> Unit) {fun1("我是参数")}//高阶函数private fun testC(fun1: (String) -> String) {val res = fun1("我是参数")println(res)}

2.3、Lambda表达式有什么作用?

可以大大的简化代码,当高阶函数与Lambda表达式结合,可以减少代码行数,让代码看起来更加简洁,比如我们常见到的一些MutableLiveData类型的监听等等。

举个例子,监听MutableLiveData类型的变量需要注册监听,看看原始写法:

	val res= MutableLiveData<Long>()res.observe(this,object:Observer<Long> {override fun onChanged(t: Long?) {TODO("Not yet implemented")}})

注意:在Kotlin中定义匿名内部类需要使用object关键字。
上面这段代码采用Lambda简化后的代码,如下:

	val res= MutableLiveData<Long>()res.observe(this) {TODO("Not yet implemented")}

效果立竿见影,超预期的简洁。

3、内联函数

3.1、什么是内联函数?

内联函数主要涉及到三个关键字:

inline(内联) ,作用于函数;

noinline(禁止内联),作用于函数类型的参数;

crossinline(限制lambda表达式直接调用 return,导致程序执行[逻辑异常],作用于函数类型的参数。

3.1.1、inline内联函数的写法

举个例子,如下:

private inline fun testH1( arg:String) {println("内联函数 testH1")}

直接在函数 fun 之前添加inline即可,这样写虽然也可以,但是AS会报一个提示:

Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types

内联对性能的预期影响微不足道。内联最适合具有函数类型参数的函数

所以内联函数是为了提高高阶函数的性能,所以推荐在带有函数类型参数的地方使用。

我们改变一下例子,最终如下:

  private inline fun testH( fun1: (String) -> Unit) {println("内联函数 testH")fun1("testD 传递的参数")}

fun1是我们的函数类型参数,这时候使用inline关键字就不会再出现上面的提示了。

3.1.2、noinline禁止内联的写法

举个例子,如下:

	private inline fun testD(noinline fun1: (String) -> Unit) {println("inline testD")fun1("testD 传递的参数")}

需要在inline函数的,函数类型的参数添加noinline关键字即可,这样写虽然也可以,但是AS又会报提示:

Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types

内联对性能的预期影响微不足道。内联最适合具有函数类型参数的函数

所以内联函数是为了提高高阶函数的性能,所以推荐在带有2个及以上的函数类型参数的时候使用noinline,这个有意思。

我们改变一下例子,最终如下:

	private inline fun testD( fun1: (String) -> Unit,noinline fun2: (String) -> String) {println("inline testD")fun1("testD 传递的参数")}

3.1.3、crossinline限制Lambda表达式直接调用 return的写法

其中前面提到的[逻辑异常]的意思是,如果在Lambda表达式中执行了return操作,会直接导致调用lambda表达式所在的函数直接终止返回。如果把lambda表达式看作子函数,这个子函数一般会在一个函数内进行使用,我们把这个子函数所在的这个函数看作父函数,这时如果在子函数中执行了return语句,则会发生奇怪的现象,父函数也立即执行了return语句。这时候想一想,如果你在主函数顺序执行多个子函数的调用,其中一个子函数是内联函数它最先被调用,如果这个内联的子函数中直接执行了return语句,那么其后的其他子函数将不会被执行。因此通过添加这个crossinline关键字就能解决这个问题,添加后会直接让内联函数的return语句编译报错,如下所示:
在这里插入图片描述

‘return’ is not allowed here

,编译器直接不允许这种写法。

举个正确的例子,写法如下:

   private inline fun testF(crossinline fun1: (String) -> String) {println("testF 之前")val res = fun1("这是 testF 传递的参数")println(res)println("testF 之后")}

也需要在inline标注的内联函数中,使用crossinline关键字,防止Lambda表达式直接调用 return语句,主打纠错。

3.3、如何使用?

举一个例子,把三个关键字都用上了,如下所示:

override fun onCreate(savedInstanceState: Bundle?) {this.savedInstanceState = savedInstanceStatetestD("普通参数", { arg: String ->println("Lambda表达式对应fun1 $arg")//return //放开就会被crossinline检测到并报错}, { arg: String ->println("Lambda表达式对应fun2$arg")val resArg = "这个高阶函数fun2是带返回值$arg"resArg})}private inline fun testD(arg: String,crossinline fun1: (String) -> Unit,noinline fun2: (String) -> String) {println("内联函数 testD$arg")fun1("testD 传递给fun1的参数")fun2("testD 传递给fun2的参数")}

3.2、内联函数有什么作用?

使用高阶函数很方便,但会带来一定的性能损耗,为了解决这个问题可以使用内联函数,主打与高阶函数进行配合。

3.3.1、高阶函数为什么会造成性能损耗?

为什么说高阶函数会导致性能损耗,我们需要看下kt文件编译后生成的.java文件,进行对比就能看出端倪,开始分析。

首先,准备一份,kt文件原始代码,如下:

	override fun onCreate(savedInstanceState: Bundle?) {this.savedInstanceState = savedInstanceStatetestX(::testY)}//高阶函数 ,无内联private fun testX(fun1: (String) -> Unit) {println("这是testX")fun1("来自testX的参数")}private fun testY(str:String) {println("这是testY")}

其次,kt文件直接调用高阶函数,查看编译后生成的.java代码,如下所示:

override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)this.testX((Function1)(new Function1(this) {//这个是重写的父类函数public Object invoke(Object p1) {this.invoke((String)p1);return Unit.INSTANCE;}//在这个匿名内部类中又临时创建了一个私有函数 invoke,并方便上面的重写的invoke函数调用 ,就是这一句this.invoke((String)p1);public final void invoke(String p0) {Intrinsics.checkNotNullParameter(p0, "p0");//这个地方调用了testY((IndexActivity)this.receiver).testY(p0);}}));}private final void testX(Function1 fun1) {String var2 = "这是testX";System.out.println(var2);fun1.invoke("来自testX的参数");}private final void testY(String str) {String var2 = "这是testY";System.out.println(var2);}

最后得出结论:
可以看到使用高阶函数后,编译的代码,直接new 了一个内部类Function1,并重写 public operator fun invoke(p1: P1): R 这个函数,同时在内部类中又单独写了一个私有函数public final void invoke(String p0) ,导致性能开销过大,所以才又内联函数的出现。

3.3.2、使用内联函数为什么可以解决高阶函数性能损耗问题?

首先,修改testX为内联函数,如下所示:

	//高阶函数 ,有内联private inline fun testX(fun1: (String) -> Unit) {println("这是testX")fun1("来自testX的参数")}

其次,使用内联函数后,查看编译生成的.java代码,如下所示:

override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)//就是这个地方int $i$f$testX = false;String var3 = "这是testX";System.out.println(var3);String p0 = "来自testX的参数";int var5 = false;this.testY(p0);}private final void testX(Function1 fun1) {int $i$f$testX = false;String var3 = "这是testX";System.out.println(var3);fun1.invoke("来自testX的参数");}private final void testY(String str) {String var2 = "这是testY";System.out.println(var2);}

最后得出结论:
可以看到添加内联函数后,代码直接合并在一起了,变成了和我们用java调用普通函数一样,不会创建新的匿名内部类对象,造成性能开销,这就是内联函数的作用。

友情提示,查看kt编译后转成.java代码的方法,如下图所示:
在这里插入图片描述
最后,再点击弹出界面中的[Decompile]按钮即可生成。

4、总结

高阶函数及Lambda表达式的引入增加了kotlin的调用灵活性及代码的简洁性,但是又带来了性能上的问题,所以又引入了内联函数,内联函数保持和java调用函数一样的高效。

使用高阶函数一定要注意要做性能优化,也就是使用内联函数来配套使用。

5、参考链接

1、【Kotlin】Kotlin的高阶函数与Lambda表达式






原创不易,求个关注。

在这里插入图片描述

微信公众号:一粒尘埃的漫旅
里面有很多想对大家说的话,就像和朋友聊聊天。
写代码,做设计,聊生活,聊工作,聊职场。
我见到的世界是什么样子的?
搜索关注我吧。

公众号与博客的内容不同。

这篇关于Kotlin高阶函数与Lambda表达式及内联函数的介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1146280

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现