前言
一、Kotin的lambda
二、let函数
三、with函数
四、 run函数
五、 apply函数
六、 also函数
七、总
相比Java语言Kotlin更加简洁安全高效。不仅支持lambda表达式,还支持非空验证机制,另外提供一套源码标准库(Standard.kt)。但是,对于Kotlin的初学者来说,可能会经常写编写出不够优雅的代码,而Kotlin提供了一些内置函数便可以帮助优化代码。
Standard.kt是Kotlin库的一部分,它定义了一些基本函数。 这个源代码文件虽然一共不到50行代码,但是这些函数功能都非常强大。
在Kotlin中对Java中的一些的接口的回调可以使用一个lambda函数做一些优化,减少一些不必要的嵌套回调方法。
但是需要注意lambda表达式的规则,只支持单抽象方法模型,不支持多个回调方法。
1、Java代码接口的回调
mView.setEventListener(new ExamPlanHomeEventListener(){
public void onSuccess(Data data){
//TODO
}
});
2、Kotlin实现接口的回调
mView.setEventListener(object: ExamPlanHomeEventListener{
public void onSuccess(Data data){
//TODO
}
});
mView.setEventListener({
data: Data ->
//TODO
})
//或者可以直接省略Data,借助kotlin的智能类型推导
mView.setEventListener({
data ->
//TODO
})
//如果以上代码中的data参数没有使用到的话,可以直接把data去掉
mView.setEventListener({
//TODO
})
//或
mView.setEventListener(){
//TODO
}
//只有一个参数,可以直接省略圆括号
mView.setEventListener{
//todo
}
可见十分的简洁。
let函数实际上是一个作用域函数,当需要去定义一个变量在一个特定的作用域范围内时,就可以使用let函数。let函数只接收一个 Lambda 参数,并且会在 Lambda 函数块中提供调用对象的上下文。let函数另一个作用就是可以避免写一些判断null的操作。
//在函数体内使用it替代object对象去访问其公有的属性和方法
object.let{
it.todo()
...
}
//另一种用途 判断object为null的操作
object?.let{
it.todo()//表示object不为null的条件下,才会去执行let函数体
}
//使用let前
mVideoPlayer?.setVideoView(activity.course_video_view)
mVideoPlayer?.setControllerView(activity.course_video_controller_view)
mVideoPlayer?.setCurtainView(activity.course_video_curtain_view)
//使用let后
mVideoPlayer?.let {
it.setVideoView(activity.course_video_view)
it.setControllerView(activity.course_video_controller_view)
it.setCurtainView(activity.course_video_curtain_view)
}
从源码let函数的结构来看它是只有一个lambda函数块block作为参数的函数。调用T类型对象的let函数,则该对象为函数的参数。在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式。
场景一: 最常用于使用let函数处理需要针对一个可null的对象统一做判空处理。
场景二: 需要去明确一个变量所处特定的作用域范围内。
with函数用于对同一个对象执行多次操作而不需要反复把对象的名称写出来,在代码中,with 可以理解为“对于 ”这个对象执⾏以下操作。
with函数接收两个参数:第一个参数可以是一个任意类型的对象T,第二个参数是一个 Lambda 函数块。 with 函数会在 Lambda 函数块中提供第一个参数对象的上下文。
with(object){
//todo
}
//源码
@kotlin.internal.InlineOnly
public inline fun with(receiver: T, block: T.() -> R): R = receiver.block()
使用with前
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
ArticleSnippet item = getItem(position);
if (item == null) {
return;
}
holder.tvNewsTitle.setText(StringUtils.trimToEmpty(item.titleEn));
holder.tvNewsSummary.setText(StringUtils.trimToEmpty(item.summary));
String gradeInfo = "难度:" + item.gradeInfo;
String wordCount = "单词数:" + item.length;
String reviewNum = "读后感:" + item.numReviews;
String extraInfo = gradeInfo + " | " + wordCount + " | " + reviewNum;
holder.tvExtraInfo.setText(extraInfo);
...
}
使用with后
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
with(item){
holder.tvNewsTitle.text = StringUtils.trimToEmpty(titleEn)
holder.tvNewsSummary.text = StringUtils.trimToEmpty(summary)
holder.tvExtraInf.text = "难度:$gradeInfo | 单词数:$length | 读后感: $numReviews"
}
}
with函数和前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式。
适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上。
run函数实际上可以说是let和with函数两个函数的结合体。run 函数只接收一个 Lambda 参数,并且会在 Lambda 函数块中提供调用对象的上下文。
object.run{
//todo
}
//源码
@kotlin.internal.InlineOnly
public inline fun T.run(block: T.() -> R): R = block()
使用run前
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
with(item){
holder.tvNewsTitle.text = StringUtils.trimToEmpty(titleEn)
holder.tvNewsSummary.text = StringUtils.trimToEmpty(summary)
holder.tvExtraInf = "难度:$gradeInfo | 单词数:$length | 读后感: $numReviews"
}
}
使用run后
override fun onBindViewHolder(holder: ViewHolder, position: Int){
getItem(position)?.run{
holder.tvNewsTitle.text = StringUtils.trimToEmpty(titleEn)
holder.tvNewsSummary.text = StringUtils.trimToEmpty(summary)
holder.tvExtraInf = "难度:$gradeInfo | 单词数:$length | 读后感: $numReviews"
}
}
run函数实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式。
适用于let、with函数任何场景。因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理。
apply 函数和 run 函数也是极其类似的,都要在某个对象上调用,并且只接收一个 Lambda 参数,也会在 Lambda 函数块中提供调用对象的上下文。但是 apply 函数无法指定返回值,而是会自动返回调用的对象本身。
object.apply{
//todo
}
//源码
@kotlin.internal.InlineOnly
public inline fun T.apply(block: T.() -> Unit): T { block(); return this }
使用apply前
if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) {
//todo
return;
}
if (mSectionMetaData.questionnaire.userProject != null) {
//todo
return;
}
if (mSectionMetaData.section != null && !mSectionMetaData.section.sectionArticles.isEmpty()) {
//todo
return;
}
使用apply后
mSectionMetaData?.apply{
//mSectionMetaData不为空的时候操作mSectionMetaData
}?.questionnaire?.apply{
//mSectionMetaData.questionnaire不为空的时候操作questionnaire
}?.section?.apply{
//section不为空的时候操作section
}?.sectionArticle?.apply{
//sectionArticle不为空的时候操作sectionArticle
}
从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身。
基于apply返回传入对象的本身,它适用场景稍微与run函数有点不一样。apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。
使用apply前
mSheetDialogView = View.inflate(activity,R.layout.biz_exam_plan_layout_sheet_inner, null)
mSheetDialogView.course_comment_tv_label.paint.isFakeBoldText = true
mSheetDialogView.course_comment_tv_score.paint.isFakeBoldText = true
mSheetDialogView.course_comment_tv_cancel.paint.isFakeBoldText = true
mSheetDialogView.course_comment_tv_confirm.paint.isFakeBoldText = true
mSheetDialogView.course_comment_seek_bar.max = 10
mSheetDialogView.course_comment_seek_bar.progress = 0
使用之后
mSheetDialogView = View.inflate(activity, R.layout.biz_exam_plan_layout_sheet_inner, null)
.apply{
course_comment_tv_label.paint.isFakeBoldText = true
course_comment_tv_score.paint.isFakeBoldText = true
course_comment_tv_cancel.paint.isFakeBoldText = true
course_comment_tv_confirm.paint.isFakeBoldText = true
course_comment_seek_bar.max = 10
course_comment_seek_bar.progress = 0
}
also函数和let函数很像,在Lambda函数块内通过 it 指代该对象。不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象,这一点和apply函数很像。
object.also{
//todo
}
//源码
@SinceKotlin(“1.1”)
public inline fun T.also(block: (T) -> Unit): T { block(this); return this }
使用also前
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
String var2 = "testLet";
int var4 = var2.length();
System.out.println(var4);
System.out.println(var2);
}
使用also后
fun main(args: Array) {
val result = "testLet".also {
println(it.length)
1000
}
println(result)
}
also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身
适用于let函数的任何场景。also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用
函数名 | 对象引用 | 返回值 | 是否可扩展函数 | 场景 |
---|---|---|---|---|
let | it指代当前对象 | 闭包形式返回 | 是 | 适用于处理不为null的操作场景 |
with | tthis指代当前对象或者省略 | 闭包形式返回 | 否 | 适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可 |
run | this指代当前对象或者省略 | 闭包形式返回 | 是 | 适用于let,with函数任何场景。 |
apply | this指代当前对象或者省略 | 上下文对象this | 是 | 对象配置 |
also | it指代当前对象 | 上下文对象this | 是 | 适用于let函数的任何场景,一般可用于多个扩展函数链式调用 |