Kotlin(1)-lambda表达式和高阶函数操作符,真的已经讲烂了-程序员宅基地

技术标签: 程序员  android  kotlin  开发语言  

官方高阶函数

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

  • run
  • with
  • apply
  • also
  • let
  • takeif 和 takeunless
  • repeat
  • lazy
run函数详解

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

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

如上所示:

run函数分为两类

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

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

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

阅读源码:

@kotlin.internal.InlineOnly
public inline fun 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 {
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 {
return block()
}
public inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}

只有一个区别:run的block执行不需要参数,而let 的block执行时需要传入this。

造成差异为:

A().run {
println(“最后一行为返回值”)
this
}.showA()

A().let {
println(“最后一行为返回值”)
it
}.showA()

run{} 作用域中 只能通过this.xxx操作当前对象,let 则用 it.xxx

takeif 和 takeunless

这两个作用相反,并且他们必须依赖对象。看源码:

public inline fun T.takeIf(predicate: (T) -> Boolean): T? {
return if (predicate(this)) this else null
}
public inline fun T.takeUnless(predicate: (T) -> Boolean): T? {
return if (!predicate(this)) this else null
}

predicate 是 (T)->Boolean 类型的lambda表达式,表示断言判断,如果判断为true,则返回自身,否则返回空

class A {
val a = 0
val b = “b”

fun showA() {
println(“$a”)
}

fun showB() {
println(“$a $b”)
}
}

fun testTakeIfAndTakeUnless() {
println(“test takeIf”)
A().takeIf { it.a > 0 }?.showB()
println(“==========”)
println(“test takeUnless”)
A().takeUnless { it.a > 0 }?.showB()
}

fun main() {
testTakeIfAndTakeUnless()
}

执行结果:

test takeIf

test takeUnless
0 b

takeIf / takeUnless适用于将条件判断的代码包在一个作用域{}中,然后 用 ?.xxxx的安全调用符 来 执行对象操作。

repeat

repeat是 多次循环的傻瓜版写法。

fun testRepeat() {
repeat(10) {
print("$it ")
}
}

fun main() {
testRepeat()
}

执行结果:

0 1 2 3 4 5 6 7 8 9

lazy

lazy的作用是: 延迟初始化val定义的常量。

class B {
val i: Int by lazy {
println(“执行i初始化”)
20
}

init {
println(“构造函数执行”)
}
}

如果只是初始化B对象,却没有使用到变量i, 那么延迟初始化不会执行。

fun main() {
B()
}

执行结果:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

d开发知识点,真正体系化!**

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

[外链图片转存中…(img-3pI5nxxr-1712776104807)]

[外链图片转存中…(img-nqFxiUyr-1712776104808)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/2401_84123232/article/details/137619464

智能推荐

题解 | #使用字符函数统计字符串中各类型字符的个数#-程序员宅基地

文章浏览阅读67次。大家好,我是涛哥,互联网大厂12年的老兵,某互联网大厂技术委员会委员,培养新人超百人。引流:字节跳动,海康威视,深信服,腾讯,阿里巴巴,拼多多,滴滴,京东,小米,大疆,各位大佬 你们好,请大佬们帮我做一下offer选择,个人情况,双非一本硕,学的java,但今年实在是难找工作,现有两个offer,请大佬帮我抉择一下。渣渣两个offer选择,各位路过大佬提提建议,招银网络科技(成都)年包26w,贝壳找房(北京)年包2.2*16=35.2w,考虑到未来房地产下行形势不理想,纠结。*//** * 代码中的类名、

利用ESP8266Wifi模块制作Wifi攻击“测试”_wifi pwn下载-程序员宅基地

文章浏览阅读3.2k次,点赞5次,收藏36次。这里写自定义目录标题准备工作烧录固件系统准备工作大家好!实验准备前你需要准备的设备:1.ESP8266Wifi模块 /1台;2.ESP8266烧录软件和固件;2.Mircro USB数据线 /1根;3.笔记本电脑 /1台。文章后我会将需要用到的软件工具及固件上传至云盘共大家学习研究。烧录固件系统1.笔记本电脑安装CH340串口驱动;2.用Mircro USB2.0数据线连接好ESP8266Wifi模块和笔记本电脑;3.运行ESP8266Flasher.exe烧录工具—>选择Con_wifi pwn下载

银河麒麟V10,Qt5.9.9,UnixOdbc,编译QODBC的libqsqlodbc.so_linux qt5 编译数据库-程序员宅基地

文章浏览阅读1.1k次,点赞15次,收藏28次。银河麒麟V10,达梦数据库,QT5.9.9,UnixOdbc,编译QODBC驱动_linux qt5 编译数据库

(opencv)任意四边形网格划分_opencv 网格-程序员宅基地

文章浏览阅读3.5k次,点赞3次,收藏19次。1 算法介绍算法功能:用于对任意四边形进行网格化处理输入:四边形的四个角点(类型为cv::Point),网格行数,网格列数输出:网格交叉点点集或网格中心点点集(类型为std::vector< cv::Point > )效果如下:vector gridret=Gridding(TEST_P0,TEST_P1,TEST_P2,TEST_P3,3,5,1);vector gri..._opencv 网格

求最小公倍数的python代码_python怎么求最大公约数和最小公倍数-程序员宅基地

文章浏览阅读922次。python怎么求最大公约数和最小公倍数一、求最大公约数用辗转相除法求最大公约数的算法如下:两个正整数a和b(a>b),它们的最大公约数等于a除以b的余数c和b之间的最大公约数。比如10和25,25除以10商2余5,那么10和25的最大公约数,等同于10和5的最大公约数。具体代码如下:def gongyue(a, b):"""欧几里得算法----辗转相除法:param a: 第一个数:par..._python最小公倍数和最大公约数代码

CUDA安装及环境配置——最新详细版-程序员宅基地

文章浏览阅读10w+次,点赞172次,收藏626次。在安装之前呢,我们需要确定三件事第一:查看显卡支持的最高CUDA的版本,以便下载对应的CUDA安装包第二:查看对应CUDA对应的VS版本,以便下载并安装对应的VS版本(vs需要先安装)第三:确定CUDA版本对应的cuDNN版本,这个其实不用太关注,因为在cudnn的下载页面会列出每个版本对应的cuda版本,11.x以上对应的范围很宽。_cuda安装

随便推点

数据结构(严蔚敏版)与算法的实现(含全部代码)_数据结构与算法严蔚敏代码csdn-程序员宅基地

文章浏览阅读10w+次,点赞788次,收藏5.4k次。目录基础c/c++ 代码优化及常见错误 c语言位运算的妙用-程序优化c/c++进制转换方法汇总(含全部代码) 二进制数-北邮2012研究生复试质因子分解除树和图外的数据结构可以使用STL: C++ STL的使用数据结构线性表顺序表 循环左移(2010联考真题)单链表 单链表相邻结点逆置(2019北邮考研真..._数据结构与算法严蔚敏代码csdn

vue-awesome-swiper安装使用时的坑_swiper\swiper.esm.js-程序员宅基地

文章浏览阅读3.7k次,点赞4次,收藏9次。关于vue-awesome-swiper官网:https://github.com/surmon-china/vue-awesome-swiper官方示例:https://github.surmon.me/vue-awesome-swiper/概述:vue-awesome-swiper是基于swiper的Vue组件。是swiper推荐的在vue中使用swiper的方式。关于SwiperSwiper 是一款免费以及轻量级的移动设备触控滑块的js框架,使用硬件加速过渡(如果该.._swiper\swiper.esm.js

vue+elementui实现el-table动态添加行数据_vue 动态el-table-程序员宅基地

文章浏览阅读2.3w次,点赞4次,收藏64次。思路:1.不管新增还是编辑都要有添加可编辑的输入行,所以在打开新增或者编辑的弹框或者页面中就要先push一个对象到table数据对象中。2.要区分我添加的行跟已完成的行。我这边定义的对象中通过设置start为0或者1的值来区分。当行中的start值为0说明是新增可编辑行,否则视为已完成的数据行3. 通过点击添加按钮把当前行的数据中的start值设置成1并且通过splice添加到数组后面中,再把table数组最后一个对象设置成空,start值改回为0;let tempRow = JSON.parse(_vue 动态el-table

Cloudify 学习(四):通过Cloudify的蓝图来创建第一个deployment,由k8s生成这个容器-容器编排_cloudiy 蓝图-程序员宅基地

文章浏览阅读1.9k次。Cloudify4.5.0和Kubernetes1.13.0进行混合容器编排demodemo背景Local Blueprint 的编写激动人心的时刻开始了,创建tomcat容器demo背景Cloudify4.5.0和Kubernetes1.13.0进行混合容器编排demo是基于前面两篇文章的环境而继续.1.安装k8s1.13.02.安装Cloudify4.5.03.Cloudify对接K..._cloudiy 蓝图

LINK : fatal error LNK1104: 无法打开文件“C:/XXX.obj”-程序员宅基地

文章浏览阅读2.8w次,点赞5次,收藏8次。问题: LINK : fatal error LNK1104: 无法打开文件“C:/XXX.obj” 遇到这样的问题时可以先看一下LINK命令行,项目->属性->链接器->命令行,在这里,前面设置的“附加依赖项”可能被IDE改写了,例如,要加入一个XXX.lib的链接库,完整路径为"C:/Program Files/XXX.lib",如果被IDE改写的话会变_link : fatal error lnk1104:

LaTeX 插入高亮代码(LaTex、Python、Java、C、C++等主流语言都支持)_latex代码引用高亮-程序员宅基地

文章浏览阅读1.2w次,点赞2次,收藏18次。相信刚入门LaTeX的盆友会感觉到LaTeX的强大之处,如果你了解markdown,那么使用LaTeX之后,你会感觉markdown除了便捷之外也就没有其它的优点了,因为LaTeX太强大了,也由于LaTeX门槛比较高,如果你使用texlive编译环境的话,那么需要安装的文件将达到7G左右,当然也有简版的。使用LaTeX也是源于自己研究生的导师,在老师的介绍下,然后不断学习,不断了解,不断使用,也..._latex代码引用高亮

推荐文章

热门文章

相关标签