Kotlin 之 let、with、run、apply、also 函数的使用
一、内联拓展函数 let
let 扩展函数的实际上是一个作用域函数,当你需要去定义一个变量在一个特定的作用域范围内,let函数的是一个不错的选择;let函数另一个作用就是可以避免写一些判断null的操作。
1.1 let 函数的使用的一般结构
object.let {
it.todo() //在函数体内使用it替代object对象去访问其公有的属性和方法
...
}
//另一种用途 判断object为null的操作
object?.let { //表示object不为null的条件下,才会去执行let函数体
it.todo()
}
1.2 let函数底层的inline扩展函数+lambda结构
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
意思就是 T 类型的对象调用 let 方法,实际调用的是传入 let 方法的 lambda 表达式的 block 块,最终返回 lambda 表达式的返回值。
lambda 表达式内部通过 it 指代该对象。
1.3 let 函数常见的适用的场景
- 场景一: 最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理。
- 场景二: 然后就是需要去明确一个变量所处特定的作用域范围内可以使用
obj?.funA()
obj?.funB()
obj?.funC()
obj?.let {
it.funA()
it.funB()
it.funC()
}
二、内联函数 with
2.1 with 函数使用的一般结构
with(object) {
//todo
}
2.2 with 函数底层的inline扩展函数+lambda 结构
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
注意,这个 with 函数不是拓展函数,它接收两个参数,第一个参数是要是用的对象,第二个参数是一个 lambda 表达式,该方法实际调用的是第一个参数对象,进行 block 块的调用,
最终返回 lambda 表达式的返回值。
lambda 表达式内部通过 this 指代该对象。
2.3 with 函数的适用的场景
适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上。
obj.funA()
obj.funB()
obj.funC()
with(obj) {
this.funA()
funB() // this 可省略
funC)
}
三、 内联拓展函数 run
3.1 run 函数使用的一般结构
object.run {
// todo
}
3.2 run 函数的inline+lambda 结构
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
run 函数实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,即返回 lambda 表达式的返回值。
3.3 run 函数的适用场景
obj?.funA()
obj?.funB()
obj?.funC()
obj?.run {
this.funA()
funB() // this 可省略
funC)
}
四、内联拓展函数 apply
4.1 apply 函数使用的一般结构
object.apply {
// todo
}
4.2 apply 函数的inline+lambda结构
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身。
五、内联扩展函数 also
5.1 also 函数使用的一般结构
object.also {
// todo
}
5.2 also 函数的inline+lambda结构
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身。
六、比较总结
| 函数名 | 定义inline的结构 | 函数体内使用的对象 | 返回值 | 是否是扩展函数 |
|---|---|---|---|---|
| let | fun T.let(block: (T) -> R): R = block(this) | it指代当前对象 | 闭包形式返回 | 是 |
| with | fun with(receiver: T, block: T.() -> R): R = receiver.block() | this指代当前对象或者省略 | 闭包形式返回 | 否 |
| run | fun T.run(block: T.() -> R): R = block() | this指代当前对象或者省略 | 闭包形式返回 | 是 |
| apply | fun T.apply(block: T.() -> Unit): T { block(); return this } | this指代当前对象或者省略 | 返回this | 是 |
| also | fun T.also(block: (T) -> Unit): T { block(this); return this } | it指代当前对象 | 返回this | 是 |
七、实用例子————Kotlin 实现单例模式
Kotlin 实现单例模式相对 java 来说很简单。比如通过 object, by lazy 操作,相信大家都会。但有时候,我们想要在单例初始化的时候顺便做一下其它初始化,极有可能会还需要传入参数。
使用 java 时,我最喜欢的实现单例模式是静态内部类的方式,但在 Android 中经常在初始化的时候需要传入 context ,然后选择了双重检查锁方式。
先看 java 代码:
public class Singleton {
private Singleton() {
}
/**
* volatile is since JDK5
*/
private static volatile Singleton sSingleton;
public static Singleton getInstance() {
if (sSingleton == null) {
synchronized (Singleton.class) {
// 未初始化,则初始instance变量
if (sSingleton == null) {
sSingleton = new Singleton();
}
}
}
return sSingleton;
}
}
再看看我们用 kotlin 实现
class Singleton private constructor(){
companion object {
@Volatile
private var instance: Singleton? = null
fun getInstance(context: Context): Singleton {
return instance?: synchronized(this) {
instance?:Singleton().also {
instance = it
}
}
}
}
}
如果要做初始化操作,我们完全可以在 also 函数里面去处理。
Kotlin 之 let、with、run、apply、also 函数的使用的更多相关文章
- JavaScript学习笔记(二)——闭包、IIFE、apply、函数与对象
一.闭包(Closure) 1.1.闭包相关的问题 请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9:方法:找到所有的div, ...
- JavaScript学习总结(二)——闭包、IIFE、apply、函数与对象
一.闭包(Closure) 1.1.闭包相关的问题 请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9:方法:找到所有的div, ...
- R中的apply族函数和多线程计算
一.apply族函数 1.apply 应用于矩阵和数组 # apply # 1代表行,2代表列 # create a matrix of 10 rows x 2 columns m <- ma ...
- Kotlin的Reified类型:怎样在函数内使用这一类型(KAD 14)
作者:Antonio Leiva 时间:Mar 2, 2017 原文链接:https://antonioleiva.com/reified-types-kotlin/ 对于Java开发者来说,最懊恼的 ...
- WScript.Shell对象的 run()和exec()函数使用详解
WScript.Shell对象的 run()和exec()函数使用详解 http://blog.sina.com.cn/s/blog_6e14a2050102v47g.html vbScript ...
- 利用call与apply向函数传递参数
Js中函数对象都有call与apply两个方法属性,二者使用方法和功能一样,只是传递参数的格式不同,call逐个传递单个参数,apply一次性传递一个参数数组. 这两个方法可以改变函数的调用对象,并且 ...
- call,apply,bind函数
一.call函数 a.call(b); 简单的理解:把a对象的方法应用到b对象上(a里如果有this,会指向b) call()的用法:用在函数上面 var Dog=function(){ this.n ...
- R中apply等函数用法[转载]
转自:https://www.cnblogs.com/nanhao/p/6674063.html 1.apply函数——对矩阵 功能是:Retruns a vector or array or lis ...
- js apply / call 函数
这两个函数的作用是: 将函数绑定到另外一个对象上去运行 用call和apply应用另一个函数(类)以后,当前的函数(类)就具备了另一个函数(类)的方法或是属性,这也能够称之为“继承”. functio ...
- 原生JS实现call,apply,bind函数
1. 前言 使用原生JS实现call和apply函数,充分了解其内部原理.call和apply都是为了解决改变this的指向.作用都相同,只是传参的方式不同.除了第一个参数外,call可以接受一个参数 ...
随机推荐
- 【SVN】Please execute the 'Cleanup' command.
背景 项目有个新的bug,我需要提取一个新的分支,但是提取之后,更新分支出现了这个问题 Please execute the 'Cleanup' command. 原因 由于使用SVN更新文件出错,导 ...
- 九鼎RK3399笔记一:Linux平台手册
@ 目录 一.git下载九鼎SDK 二.安装所需的软件包: 三.安装 kernel 及 u-boot 编译需要依赖的软件包 四.安装文件系统需要依赖的软件包 五.安装 Buildroot 编译需要依赖 ...
- 6月4日 python学习总结 初次接触jQuery
1. jQuery是什么?是一个轻量级的,兼容多浏览器的JS库(write less, do more) 1. 是一个工具,简单方便的实现一些DOM操作 2. 不用jQuery完全可以,但是不明智. ...
- CVE-2010-2861(Adobe ColdFusion 文件读取漏洞)
漏洞介绍 Adobe ColdFusion是美国Adobe公司的一款动态Web服务器产品,其运行的CFML(ColdFusion Markup Language)是针对Web应用的一种程序设计语言. ...
- uoj310. 【UNR #2】黎明前的巧克力
题目描述: uoj 题解: WTF. 看题解看了一个小时才看明白. 首先有状态$f[i][j]$表示前$i$个东西两人取,最后两人异或和为$j$的有多少方案. 转移为$f[i][j]=f[i-1][j ...
- 职场PUA
哈哈 你这个的底层逻辑是什么? 顶层设计在哪? 最终交付价值是什么? 过程的抓手在哪里? 如何保证结果的闭环? 你比别人的亮点在哪里? 优势在哪里? 你的思考和沉淀在哪里? 你有形成自己的方法论吗?
- github新手使用指南
常用命令: Git 速查表(摘自 AI有道) 一.常见命令 git init : 初始化 git 仓库,即将一个文件夹初始化为一个 git 仓库.具体的操作是创建一个 .git 隐藏文件夹 git ...
- 你如何在 Java 中获取线程堆栈?
kill -3 [java pid] 不会在当前终端输出,它会输出到代码执行的或指定的地方去.比如,kill -3 tomcat pid, 输出堆栈到 log 目录下. Jstack [java pi ...
- Spring框架的事务管理有哪些优点?
它为不同的事务API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式. 它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如 它支持声明式事务管理 ...
- Redis 相比 Memcached 有哪些优势?
1.Memcached 所有的值均是简单的字符串,redis 作为其替代者,支持更为丰 富的数据类 2.Redis 的速度比 Memcached 快很 3.Redis 可以持久化其数据