文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

一篇带给你Kotin高阶函数详解

2024-12-03 00:17

关注

今天我们来讲解高阶函数

一、高阶函数详解

1、高阶函数是什么?

  1. public fun test2(test:Int,block:()->Unit){ 
  2.  
  3. var v= block() 
  4.  
  5. DTLog.i("TestTest","Test1"
  6.  
  7.  
  8. public fun T.test22(block:()->T):T{ 
  9.  
  10. return block() 
  11.  
  12.  
  13. public fun T.test26(block:T.()->Unit){ 
  14.  
  15. block() 
  16.  
  17.  
  18. public fun T.test23(block:(T)->Unit):T{ 
  19.  
  20. return this 
  21.  
  22.  
  23. public fun 
  24.  
  25. var t=block(this) 
  26.  
  27. return t 
  28.  
  29.  
  30. public fun 
  31.  
  32. return block(this) 
  33.  

以上就是一个高阶函数,它接收了一个函数类型的参数,而调用高阶函数的方法与调用普通函数差异不大,只需要在参数名后面加上括号,并在括号中传入必要的参数即可;

高阶函数类型具有与函数签名相对应的特殊表示法,即它们的参数和返回值:

2、内联函数详解

①内联函数是什么

inline(小心,不是online),翻译成“内联”或“内嵌”。意指:当编译器发现某段代码在调用一个内联函数时,它不是去调用该函数,而是将该函数的代码,整段插入到当前位置。这样做的好处是省去了调用的过程,加快程序运行速度。(函数的调用过程,由于有前面所说的参数入栈等操作,所以总要多占用一些时间)。这样做的不好处:由于每当代码调用到内联函数,就需要在调用处直接插入一段该函数的代码,所以程序的体积将增大。拿生活现象比喻,就像电视坏了,通过电话找修理工来,你会嫌慢,于是干脆在家里养了一个修理工。这样当然是快了,不过,修理工住在你家可就要占地儿了。内联函数并不是必须的,它只是为了提高速度而进行的一种修饰。要修饰一个函数为内联型

使用如下格式:

inline 函数的声明或定义

简单一句话,在函数声明或定义前加一个 inline 修饰符。

  1. inline int max(int a, int b) 
  2.  
  3.  
  4. return (a>b)? a : b; 
  5.  

内联函数的本质是,节省时间但是消耗空间。

②内联函数规则

inline函数的规则

(1)、一个函数可以自已调用自已,称为递归调用(后面讲到),含有递归调用的函数不能设置为inline;

(2)、使用了复杂流程控制语句:循环语句和switch语句,无法设置为inline;

(3)、由于inline增加体积的特性,所以建议inline函数内的代码应很短小。最好不超过5行。

(4)、inline仅做为一种“请求”,特定的情况下,编译器将不理会inline关键字,而强制让函数成为普通函数。出现这种情况,编译器会给出警告消息。

(5)、在你调用一个内联函数之前,这个函数一定要在之前有声明或已定义为inline,如果在前面声明为普通函数,而在调用代码后面才定义为一个inline函数,程序可以通过编译,但该函数没有实现inline。比如下面代码片段:

  1. //函数一开始没有被声明为inline: 
  2.  
  3. void foo(); 
  4.  
  5. //然后就有代码调用它: 
  6.  
  7. foo(); 
  8.  
  9. //在调用后才有定义函数为inline: 
  10.  
  11. inline void foo() 
  12.  
  13.  
  14. ...... 
  15.  

代码是的foo()函数最终没有实现inline;

(6)、为了调试方便,在程序处于调试阶段时,所有内联函数都不被实现

③内联函数时应注意以下几个问题

(1) 在一个文件中定义的内联函数不能在另一个文件中使用。它们通常放在头文件中共享。

(2) 内联函数应该简洁,只有几个语句,如果语句较多,不适合于定义为内联函数。

(3) 内联函数体中,不能有循环语句、if语句或switch语句,否则,函数定义时即使有inline关键字,编译器也会把该函数作为非内联函数处理。

(4) 内联函数要在函数被调用之前声明。关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用。

3、高阶函数中使用内联函数

直使用的 Lambda 表达式在底层被转换成了匿名类的实现方式。这就表明,我们每调用一次 Lambda 表达式,都会创建一个新的匿名类实例,当然也会造成额外的内存和性能开销。为了解决这个问题,Kotlin 提供了内联函数的功能,它可以将使用 Lambda 表达式带来的运行时开销完全消除,只需要在定义高阶函数时加上 inline 关键字的声明即可

  1. inline fun test111(num1: Int, num2: Int, block: (IntInt) -> Int): Int { 
  2.  
  3. val result = block(num1, num2) 
  4.  
  5. return result 
  6.  

4、闭包函数

闭包函数 一个函数的返回值是函数,函数的内部包含另一个函数,可以是有参无参的匿名函数

  1. fun main(args: Array) { 
  2.  
  3. val mm = aaa() 
  4.  
  5. println(mm()) 
  6.  
  7. println(mm()) 
  8.  
  9. println(mm()) 
  10.  
  11. println(mm()) 
  12.  
  13. println(mm()) 
  14.  
  15. val kk = bbb() 
  16.  
  17. println(kk("shadow")) //shadow --- 1 
  18.  
  19. println(kk("shadow")) //shadow --- 2 
  20.  
  21. println(kk("shadow")) //shadow --- 3 
  22.  
  23. println(kk("shadow")) //shadow --- 4 
  24.  
  25. println(kk("shadow")) //shadow --- 5 
  26.  
  27.  
  28. //闭包函数 就是函数作为返回参数 
  29.  
  30. fun aaa(): () -> (Int) { 
  31.  
  32. var current = 10 
  33.  
  34. return fun(): Int { 
  35.  
  36. return current++ 
  37.  
  38.  
  39.  
  40. fun bbb(): (String) -> (String) { 
  41.  
  42. var current = 0; 
  43.  
  44. return fun(str: String): String { 
  45.  
  46. current++; 
  47.  
  48. return "$str --- $current"
  49.  
  50.  

二、kotin中标准库Standard.kt源码讲解


在 Kotlin 源码的Standard.kt标准库中提供了一些便捷的内置高阶函数( let、also、with、run、apply ),可以帮助我们写出更简洁优雅的 Kotlin 代码,提高开发效率,学习源码可以更快的帮助我们理解和应用

1、apply

  1. @kotlin.internal.InlineOnly 
  2.  
  3. public inline fun T.apply(block: T.() -> Unit): T { 
  4.  
  5. contract { 
  6.  
  7. callsInPlace(block, InvocationKind.EXACTLY_ONCE) 
  8.  
  9.  
  10. block() 
  11.  
  12. return this 
  13.  
  1. val str = "hello" 
  2.  
  3. str.apply { length } //可以省略 str. 
  4.  
  5. str.apply { this.length } //可以这样 

2、let

  1. @kotlin.internal.InlineOnly 
  2.  
  3. public inline fun 
  4.  
  5. contract { 
  6.  
  7. callsInPlace(block, InvocationKind.EXACTLY_ONCE) 
  8.  
  9.  
  10. return block(this) 
  11.  

3、also

  1. @kotlin.internal.InlineOnly 
  2.  
  3. @SinceKotlin("1.1"
  4.  
  5. public inline fun T.also(block: (T) -> Unit): T { 
  6.  
  7. contract { 
  8.  
  9. callsInPlace(block, InvocationKind.EXACTLY_ONCE) 
  10.  
  11.  
  12. block(this) 
  13.  
  14. return this 
  15.  

执行一个 T 类型中的方法,变量等,然后返回自身 T;

传递it作为block函数参数(调用时不可以省略),且also函数的返回值是调用者本身;

这个方法与上面的 apply 方法类似,只是在大括号中执行 T 自身方法的时候,必须要加上 T. 否则无法调用 T 中的 API,什么意思呢?看下面代码:

  1. val str = "hello" 
  2.  
  3. str.also { str.length } //str.必须加上,否则编译报错 
  4.  
  5. str.also { it.length } //或者用 it. 

4、with

  1. @kotlin.internal.InlineOnly 
  2.  
  3. public inline fun 
  4.  
  5. contract { 
  6.  
  7. callsInPlace(block, InvocationKind.EXACTLY_ONCE) 
  8.  
  9.  
  10. return receiver.block() 
  11.  
  1. val str = "hello" 
  2.  
  3. val ch = with(str) { 
  4.  
  5. get(0) 
  6.  
  7.  
  8. println(ch) //打印 h 

5、run

  1. @kotlin.internal.InlineOnly 
  2.  
  3. public inline fun run(block: () -> R): R { 
  4.  
  5. contract { 
  6.  
  7. callsInPlace(block, InvocationKind.EXACTLY_ONCE) 
  8.  
  9.  
  10. return block() 
  11.  
  1. @kotlin.internal.InlineOnly 
  2.  
  3. public inline fun 
  4.  
  5. contract { 
  6.  
  7. callsInPlace(block, InvocationKind.EXACTLY_ONCE) 
  8.  
  9.  
  10. return block() 
  11.  
  1. run { 
  2.  
  3. println(888) 
  4.  
  5.  
  6. val res = run { 2 + 3 } 
  7.  
  8. fun runDemo() { 
  9.  
  10. println("测试run方法"
  11.  
  12.  
  13. //我们可以这么干 
  14.  
  15. run(::runDemo) 

6、takeIf

  1. public inline fun T.takeIf(predicate: (T) -> Boolean): T? { 
  2.  
  3. contract { 
  4.  
  5. callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) 
  6.  
  7.  
  8. return if (predicate(this)) this else null 
  9.  
  1. val str = "helloWorld" 
  2.  
  3. str.takeIf { str.contains("hello") }?.run(::println) 

7、takeUnless

  1. public inline fun T.takeUnless(predicate: (T) -> Boolean): T? { 
  2.  
  3. contract { 
  4.  
  5. callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) 
  6.  
  7.  
  8. return if (!predicate(this)) this else null 
  9.  

这个方法跟 takeIf() 方法类似,只是内部判断为false的时候返回自身T ,而 true 的时候返回 null,因此不过多说明,使用参考 takeIf() 方法。

8、repeat()

  1. public inline fun repeat(times: Intaction: (Int) -> Unit) { 
  2.  
  3. contract { callsInPlace(action) } 
  4.  
  5. for (index in 0 until times) { 
  6.  
  7. action(index
  8.  
  9.  

分析:repeat 方法包含两个参数:

  1. public inline fun repeat(times: Intaction: (Int) -> Unit) { 
  2.  
  3. contract { callsInPlace(action) } 
  4.  
  5. for (index in 0 until times) { 
  6.  
  7. action(index
  8.  
  9.  

三、高阶函数选择

 


 


总结

不管是 Kotlin 中内置的高阶函数,还是我们自定义的,其传入的代码块样式,无非以下几种:

1、block: () -> T 和 block: () -> 具体类型

这种在使用 (::fun) 形式简化时,要求传入的方法必须是无参数的,返回值类型如果是T则可为任意类型,否则返回的类型必须要跟这个代码块返回类型一致

2、block: T.() -> R 和 block: T.() -> 具体类型

这种在使用 (::fun) 形式简化时,要求传入的方法必须包含一个T类型的参数,返回值类型如果是R则可为任意类型,否则返回的类型必须要跟这个代码块返回类型一致。例如 with 和 apply 这两个方法

3、block: (T) -> R 和 block: (T) -> 具体类型

这种在使用 (::fun) 形式简化时,要求传入的方法必须包含一个T类型的参数,返回值类型如果是R则可为任意类型,否则返回的类型必须要跟这个代码块返回类型一致。例如 let 和 takeIf 这两个方法

 

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯