IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> linux教程视频,Kotlin(1)-lambda表达式和高阶函数操作符 -> 正文阅读

[移动开发]linux教程视频,Kotlin(1)-lambda表达式和高阶函数操作符

    print("求和 ")
    x + y
}
val diff = { x: Int, y: Int ->
    print("求差 ")
    x - y
}
//kotlin里面,可以把拉姆达表示当作一个普通变量一样,去传递实参
calculate(p1 = 1, p2 = 2, event1 = sum, event2 = diff)

}


定义了一个calculate函数, p1,p2 是Int,而event1和event2 则是 lambda表达式. 高级语言特性,一等函数公民:函数本身可以被当作普通参数一样去传递,并且调用。那么, kotlin的lambda内核是怎么样的?

![image](https://upload-images.jianshu.io/upload_images/15590149-d434cb0d6708d7e9?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

上图能够看出,

1. calculate方法的后面2个参数,被编译成了 Function2 类型。
2. 执行event1,event2,则是调用其invoke方法
3. main函数中,出现了null.INSTANCE, 这里比较诡异,INSTANCE应该是用来获取实例的,但是为什么是null.INSTANCE

而看了Function2的源码,简直亮瞎了我的钛合金狗眼:

![image](https://upload-images.jianshu.io/upload_images/15590149-898677d25e084399?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

Function.kt文件中:

Function接口,Function2接口.....Function22接口。好了,不要质疑谷歌大佬的设计思路,反正大佬写的就是对的...这里用22个接口(至于为什么是22个,猜想是谷歌觉得不会有哪个脑子秀逗的开发者真的弄22个以上的参数挤在一起吧),表示kotlin开发中可能出现的lambda表达式参数列表。

#### 再举个栗子

给一个Button 设置点击事件,kotlin的写法是:

```kotlin
val btn = Button(this)
val lis = View.OnClickListener { println("111") }
btn.setOnClickListener(lis)

或者:

val btn = Button(this)
btn.setOnClickListener { println("111") }

setOnClickListener 方法的参数是 OnClickListener 接口:

public interface OnClickListener {
    void onClick(View v);
}

类似这种符合lambda表达式特征的接口,都可以使用上述两种写法来大大简化代码量。

最后一个栗子

不得不提一句,lambda表达式有一个变形,那就是:当lambda表达式作为方法的最后一个参数时,可以lambda表达式放到小括号外面。而如果只有一个参数就是lambda表达式,那么括号可以省略

这个非常重要,不了解这个,很多地方都会感觉很蛋疼。

fun testLambda(s: String, block: () -> String) {
    println(s)
    block()
}

fun testLambda2(block: () -> String) {
    block()
}

fun main() {
    testLambda("第一个参数") {
        println("block函数体")
        "返回值"
    }
    testLambda2 {
        println("block函数体")
        "返回值"
    }
}

总结

Kotlin中lambda表达式可以当作普通参数一样去传递,去赋值,去使用。


高阶函数以及操作符

上文提到,kotlin中lambda表达式已经融入了语言核心,而具体的体现就是高阶函数,把lambda表达式当作参数去使用. 这种将lambda表达式作为参数或者返回值的函数,就叫高阶函数。

官方高阶函数

Kotlin谷歌已经给我们封装了一些高阶函数。

  • run
  • with
  • apply
  • also
  • let
  • takeif 和 takeunless
  • repeat
  • lazy

run函数详解

代码如下(这里为了展示代码全貌,忽视androidStudio的代码优化提示):

class A {
    val a = 1
    val b = "b"
}
fun testRun() {
    //run方法有两种用法,一个是不依赖对象,也就是作为全局函数
    run<String> {//我可以规定返回值的类型
        println("我是全局函数")
        "返回值"
    }
    val a = A()
    //另一种则是 依赖对象
    a.run<A,String> {//这里同样可以规定返回值的类型
        println(this.a)
        println(this.b)
        "返回值"
    }
}
fun main() {
    testRun()
}

如上所示:

run函数分为两类

  • 不依赖对象的全局函数。

  • 依赖对象的"类似"扩展函数。

    两者都可以规定返回值类型(精通泛型的话,这里应该不难理解,泛型下一节详解)。

阅读源码:

@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

run函数被重载,参数有所不同

  • 前者 参数类型为 ()->R ,返回值为 R ,函数体内执行block(),并且返回执行结果
  • 后者 参数类型为 T.()->R ,返回值为R , T.() 明显是依赖 T类的(貌似T的扩展函数),返回值依然是R,执行完之后返回结果。
  • 并且,可以发现 两者都是内联函数 inline (执行代码时不进行方法的压栈出栈,而是类似于直接在目标处执行代码段)

所以,前者不需要依赖对象,后者必须依赖对象(因为它是T类的"扩展函数")

使用场景

根据run方法的特性,无论是不是依赖对象,它都是封装了一段代码,而且还是inline的。所以:

  • 如果你不想把一段代码独立成方法,又不想让它们看上去这么凌乱,最重要的是告诉后来的开发者 这段代码是一个整体,不要随便给我在里面插代码,或者拆分。那就用run方法,把他们整合到一个作用域中。

    run {
        println("这是一段代码逻辑很相近的代码段")
        println("但是我不想把他独立成一个方法")
        println("又担心别人会随便改")
        println("所以用run方法把他们放在同一个作用域中")
        println("小组中其他人看到这段,就知道不要把无关代码插进来")
    }
    
  • 更神奇的是,这个run函数是有返回值的,参数返回值可以利用起来:

    fun testRun2(param1: String, param2: String, param3: String) {
        //我想让这几个参数都不为空,如果检查是空,就不执行方法主体
        val checkRes: Boolean = run<Boolean> {
            when {
                param1.isNullOrEmpty() -> {
                    false
                }
                param2.isNullOrEmpty() -> {
                    false
                }
                param3.isNullOrEmpty() -> {
                    false
                }
                else -> true
            }
        }
    
        if (checkRes){
            println("参数检查完毕,现在可以继续接下来的操作了")
        }else{
            println("参数检查不通过,不执行主体代码")
        }
    }
    fun main() {
      testRun2("1", "2", "")
    }
    

main运行结果:

参数检查完毕,现在可以继续接下来的操作了

小结论

run方法在整合小段代码的功效上,还是很实用的

其他高阶函数

上面列举出来的这些系统高阶函数原理上都差不多,只是使用场景有区别,因此除了run之外,其他的不再详解,而只说明使用场景。

apply

和run只有一个区别,run是返回block()执行之后的返回值,而,apply 则是返回this,因此 apply必须依赖对象。而由于返回了this,因此可以连续apply调用。

fun testApply() {
    val a = A()
    a.apply {
        println("如果一个对象要对它进行多个阶段的处理")
    }.apply {
        println("那么多个阶段都挤在一起则容易混淆,")
    }.apply {
        println("此时可以用apply将每一个阶段分开摆放")
    }.apply {
        println("让程序代码更加优雅")
    }
}

fun main() {
    testApply()
}
with

下面的代码应该都很眼熟,Glide图片加载框架的用法,with(context)然后链式调用

Glide.with(this).load(image).asGif().into(mImageView);

Kotlin中的with貌似把这一写法发扬光大了(只是类比,不用深究),场景如下:

class A {
    val a = 1
    val b = "b"

    fun showA() {
        println("$a")
    }

    fun showB() {
        println("$a $b")
    }
}

fun testWith() {
    val a = A()
    with(a) {
        println("作用域中可以直接引用创建出的a对象")
        this.a
        this.b
        this.showA()
        this
    }.showB()
}

fun main() {
    testWith()
}

细节

  1. with(a){} 大括号内的作用域里面,可以直接使用 当前a对象的引用,可以this.xxx 也可以 a.xxx
  2. with(a){} 大括号作用域内的最后一行是 返回值,如果我返回this,那么with结束之后,我可以继续 调用a的方法
also

also和with一样,必须依赖对象,返回值为this。因此它也支持链式调用,它和apply的区别是:

apply的block,没有参数,但是 also 则将this作为了参数。这么做造成的差异是:

作用域 { } 内调用当前对象的方式不同。

class A {
    val a = 1
    val b = "b"

    fun showA() {
        println("$a")
    }

    fun showB() {
        println("$a $b")
    }
}
fun testApply() {
    A().apply {
        this.showA()
        println("=======")
    }.showB()
}

fun testAlso() {
    A().also {
        it.showA()
        println("=======")
    }.showB()
}

apply 必须用this.xxx, also则用 it.xxx.

let

类比到run:

public inline fun <T, R> T.run(block: T.() -> R): R {


### 最后如何让自己一步步成为技术专家


说句实话,如果一个打工人不想提升自己,那便没有工作的意义,毕竟大家也没有到养老的年龄。

当你的技术在一步步贴近阿里p7水平的时候,毫无疑问你的薪资肯定会涨,同时你能学到更多更深的技术,交结到更厉害的大牛。

**推荐一份Java架构之路必备的学习笔记,内容相当全面!!!**

![](https://img-blog.csdnimg.cn/img_convert/d9724345c46bfa0e329b18f2cd63e8fb.png)

成年人的世界没有容易二字,前段时间刷抖音看到一个程序员连着加班两星期到半夜2点的视频。在这个行业若想要拿高薪除了提高硬实力别无他法。

你知道吗?现在有的应届生实习薪资都已经赶超开发5年的程序员了,实习薪资26K,30K,你没有紧迫感吗?做了这么多年还不如一个应届生,真的非常尴尬!

进了这个行业就不要把没时间学习当借口,这个行业就是要不断学习,不然就只能被裁员。所以,抓紧时间投资自己,多学点技术,眼前困难,往后轻松!

要领取这些精心整理出来的资料的话,请记得

7水平的时候,毫无疑问你的薪资肯定会涨,同时你能学到更多更深的技术,交结到更厉害的大牛。

**推荐一份Java架构之路必备的学习笔记,内容相当全面!!!**

[外链图片转存中...(img-bSNIuBcL-1628299007548)]

成年人的世界没有容易二字,前段时间刷抖音看到一个程序员连着加班两星期到半夜2点的视频。在这个行业若想要拿高薪除了提高硬实力别无他法。

你知道吗?现在有的应届生实习薪资都已经赶超开发5年的程序员了,实习薪资26K,30K,你没有紧迫感吗?做了这么多年还不如一个应届生,真的非常尴尬!

进了这个行业就不要把没时间学习当借口,这个行业就是要不断学习,不然就只能被裁员。所以,抓紧时间投资自己,多学点技术,眼前困难,往后轻松!

要领取这些精心整理出来的资料的话,请记得

**————【关注】+【转发】+【点赞】支持我!创作不易![点击这里前往我的腾讯文档免费下载](https://gitee.com/vip204888/java-p7)**
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-08 11:27:20  更:2021-08-08 11:28:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 9:50:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码