标签: Kotlin      常用技巧


目录:

  • 一、回调函数的Kotin的lambda的简化
  • 二、内联扩展函数之let
  • 三、内联函数之with
  • 四、内联扩展函数之run
  • 五、内联扩展函数之apply
  • 六、内联扩展函数之also
  • 七、let,with,run,apply,also函数区别

简述:

相比Java, Kotlin提供了不少高级语法特性。对于一个Kotlin的初学者来说经常会写出一些不够优雅的代码。在Kotlin中的源码标准库(Standard.kt)中提供了一些Kotlin扩展的内置函数可以优化kotlin的编码。Standard.kt是Kotlin库的一部分,它定义了一些基本函数。 这个源代码文件虽然一共不到50行代码,但是这些函数功能都非常强大。


一、回调函数的Kotin的lambda的简化

在Kotlin中对Java中的一些的接口的回调做了一些优化,可以使用一个lambda函数来代替。可以简化写一些不必要的嵌套回调方法。但是需要注意:在lambda表达式,只支持单抽象方法模型,也就是说设计的接口里面只有一个抽象的方法,才符合lambda表达式的规则,多个回调方法不支持。

  • 1、用Java代码实现一个接口的回调。

     mView.setEventListener(new ExamPlanHomeEventListener(){public void onSuccess(Data data){//todo}});
  • 2、在Kotlin中的实现一个接口的回调,不使用lambda表达式(这种方式非常适用于kotlin中对于一个接口中含有多个回调方法)。

    
    mView.setEventListener(object: ExamPlanHomeEventListener{public void onSuccess(Data data){//todo}});
    
  • 3、如果在Kotlin中的对于接口只有一个回调的方法,就符合使用lambda函数,我们可以把以上代码简化成这样。

    mView.setEventListener({data: Data ->//todo
    })//或者可以直接省略Data,借助kotlin的智能类型推导mView.setEventListener({data ->//todo
    })
    
  • 4、如果以上代码中的data参数没有使用到的话,可以直接把data去掉

    mView.setEventListener({//todo})
    
  • 5、以上代码还可以做个调整,由于setEventListener函数最后一个参数是一个函数的话,可以直接把括号的实现提到圆括号外面

    mView.setEventListener(){//todo
    }
    
  • 6、由于setEventListener这个函数只有一个参数,可以直接省略圆括号

    mView.setEventListener{//todo
    }
    

二、内联扩展函数之let

let扩展函数的实际上是一个作用域函数,当你需要去定义一个变量在一个特定的作用域范围内,let函数的是一个不错的选择;let函数另一个作用就是可以避免写一些判断null的操作。
  • 1、let函数的使用的一般结构
object.let{it.todo()//在函数体内使用it替代object对象去访问其公有的属性和方法...
}//另一种用途 判断object为null的操作
object?.let{//表示object不为null的条件下,才会去执行let函数体it.todo()
}
  • 2、let函数底层的inline扩展函数+lambda结构
   @kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
  • 3、let函数inline结构的分析

    从源码let函数的结构来看它是只有一个lambda函数块block作为参数的函数,调用T类型对象的let函数,则该对象为函数的参数。在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式。

  • 4、let函数的kotlin和Java转化

     //kotlinfun main(args: Array<String>) {val result = "testLet".let {println(it.length)1000}println(result)}//javapublic final class LetFunctionKt {public static final void main(@NotNull String[] args) {Intrinsics.checkParameterIsNotNull(args, "args");String var2 = "testLet";int var4 = var2.length();System.out.println(var4);int result = 1000;System.out.println(result);}
    }
  • 5、let函数适用的场景

    场景一: 最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理。

    场景二: 然后就是需要去明确一个变量所处特定的作用域范围内可以使用

  • 6、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)
}```

三、内联函数之with

  • 1、with函数使用的一般结构

     with(object){//todo}
    
  • 2、with函数底层的inline扩展函数+lambda结构

    @kotlin.internal.InlineOnly
    public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
  • 3、with函数inline结构的分析

with函数和前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式。

可以看出with函数是接收了两个参数,分别为T类型的对象receiver和一个lambda函数块,所以with函数最原始样子如下:

```val result = with(user, {println("my name is $name, I am $age years old, my phone number is $phoneNum")1000})
```

但是由于with函数最后一个参数是一个函数,可以把函数提到圆括号的外部,所以最终with函数的调用形式如下:

```
val result = with(user) {println("my name is $name, I am $age years old, my phone number is $phoneNum")1000}
```
  • 4、with函数的kotlin和Java转化

    //kotlinfun main(args: Array<String>) {val user = User("Kotlin", 1, "1111111")val result = with(user) {println("my name is $name, I am $age years old, my phone number is $phoneNum")1000}println("result: $result")
    }//javapublic static final void main(@NotNull String[] args) {Intrinsics.checkParameterIsNotNull(args, "args");User user = new User("Kotlin", 1, "1111111");String var4 = "my name is " + user.getName() + ", I am " + user.getAge() + " years old, my phone number is " + user.getPhoneNum();System.out.println(var4);int result = 1000;String var3 = "result: " + result;System.out.println(var3);}
  • 5、with函数的适用的场景

适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上

  • 6、with函数使用前后的对比

没有使用kotlin中的实现

```
@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);...
}```

kotlin的实现

```
override fun onBindViewHolder(holder: ViewHolder, position: Int){val item = getItem(position)?: returnwith(item){holder.tvNewsTitle.text = StringUtils.trimToEmpty(titleEn)holder.tvNewsSummary.text = StringUtils.trimToEmpty(summary)holder.tvExtraInf.text = "难度:$gradeInfo | 单词数:$length | 读后感: $numReviews"...   }}```

四、内联扩展函数之run

  • 1、run函数使用的一般结构

    object.run{
    //todo
    }
    
  • 2、run函数的inline+lambda结构

    @kotlin.internal.InlineOnly
    public inline fun <T, R> T.run(block: T.() -> R): R = block()
    
  • 3、run函数的inline结构分析

    run函数实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式。

  • 4、run函数的kotlin和Java转化

    //kotlinfun main(args: Array<String>) {val user = User("Kotlin", 1, "1111111")val result = user.run {println("my name is $name, I am $age years old, my phone number is $phoneNum")1000}println("result: $result")
    }//javapublic static final void main(@NotNull String[] args) {Intrinsics.checkParameterIsNotNull(args, "args");User user = new User("Kotlin", 1, "1111111");String var5 = "my name is " + user.getName() + ", I am " + user.getAge() + " years old, my phone number is " + user.getPhoneNum();System.out.println(var5);int result = 1000;String var3 = "result: " + result;System.out.println(var3);}
  • 5、run函数的适用场景

适用于let,with函数任何场景。因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理

  • 6、run函数使用前后的对比

还是借助上个例子kotlin代码

```
override fun onBindViewHolder(holder: ViewHolder, position: Int){val item = getItem(position)?: returnwith(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"...   }}```

五、内联扩展函数之apply

  • 1、apply函数使用的一般结构

    object.apply{
    //todo
    }
    
  • 2、apply函数的inline+lambda结构

    @kotlin.internal.InlineOnly
    public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
    
  • 3、apply函数的inline结构分析

从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身。

  • 4、apply函数的kotlin和Java转化

    //kotlinfun main(args: Array<String>) {val user = User("Kotlin", 1, "1111111")val result = user.apply {println("my name is $name, I am $age years old, my phone number is $phoneNum")1000}println("result: $result")
    }//javapublic final class ApplyFunctionKt {public static final void main(@NotNull String[] args) {Intrinsics.checkParameterIsNotNull(args, "args");User user = new User("Kotlin", 1, "1111111");String var5 = "my name is " + user.getName() + ", I am " + user.getAge() + " years old, my phone number is " + user.getPhoneNum();System.out.println(var5);String var3 = "result: " + user;System.out.println(var3);}
    }
  • 5、apply函数的适用场景

整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。正是基于这一点差异它的适用场景稍微与run函数有点不一样。apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到。

  • 6、apply函数使用前后的对比

没有使用apply函数的代码是这样的,看起来不够优雅

```
mSheetDialogView = View.inflate(activity, R.layout.biz_exam_plan_layout_sheet_inner, null)mSheetDialogView.course_comment_tv_label.paint.isFakeBoldText = truemSheetDialogView.course_comment_tv_score.paint.isFakeBoldText = truemSheetDialogView.course_comment_tv_cancel.paint.isFakeBoldText = truemSheetDialogView.course_comment_tv_confirm.paint.isFakeBoldText = truemSheetDialogView.course_comment_seek_bar.max = 10mSheetDialogView.course_comment_seek_bar.progress = 0
```

使用apply函数后的代码是这样的

```
mSheetDialogView = View.inflate(activity, R.layout.biz_exam_plan_layout_sheet_inner, null).apply{course_comment_tv_label.paint.isFakeBoldText = truecourse_comment_tv_score.paint.isFakeBoldText = truecourse_comment_tv_cancel.paint.isFakeBoldText = truecourse_comment_tv_confirm.paint.isFakeBoldText = truecourse_comment_seek_bar.max = 10course_comment_seek_bar.progress = 0}```

多层级判空问题

```if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) {return;}if (mSectionMetaData.questionnaire.userProject != null) {renderAnalysis();return;}if (mSectionMetaData.section != null && !mSectionMetaData.section.sectionArticles.isEmpty()) {fetchQuestionData();return;}
```

kotlin的apply函数优化

```
mSectionMetaData?.apply{//mSectionMetaData不为空的时候操作mSectionMetaData}?.questionnaire?.apply{//questionnaire不为空的时候操作questionnaire}?.section?.apply{//section不为空的时候操作section}?.sectionArticle?.apply{//sectionArticle不为空的时候操作sectionArticle}```

六、内联扩展函数之also

  • 1、also函数使用的一般结构

    object.also{
    //todo
    }
    
  • 2、also函数的inline+lambda结构

    @kotlin.internal.InlineOnly
    

@SinceKotlin(“1.1”)
public inline fun T.also(block: (T) -> Unit): T { block(this); return this }
```

  • 3、also函数的inline结构分析

also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身

  • 4、also函数编译后的class文件

    //kotlinfun main(args: Array<String>) {val result = "testLet".also {println(it.length)1000}println(result)
    }//javapublic final class AlsoFunctionKt {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);}
    }
    
  • 5、also函数的适用场景

    适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用

  • 6、also函数使用前后的对比

    和let函数类似

七、let,with,run,apply,also函数区别

通过以上几种函数的介绍,可以很方便优化kotlin中代码编写,整体看起来几个函数的作用很相似,但是各自又存在着不同。使用的场景有相同的地方比如run函数就是let和with的结合体。下面一张表格可以清晰对比出他们的不同之处。

函数名 定义inline的结构 函数体内使用的对象 返回值 是否是扩展函数 适用的场景
let fun <T, R> T.let(block: (T) -> R): R = block(this) it指代当前对象 闭包形式返回 适用于处理不为null的操作场景
with fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block() this指代当前对象或者省略 闭包形式返回 适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上
run fun <T, R> T.run(block: T.() -> R): R = block() this指代当前对象或者省略 闭包形式返回 适用于let,with函数任何场景。
apply fun T.apply(block: T.() -> Unit): T { block(); return this } this指代当前对象或者省略 返回this 1、适用于run函数的任何场景,一般用于初始化一个对象实例的时候,操作对象属性,并最终返回这个对象。
2、动态inflate出一个XML的View的时候需要给View绑定数据也会用到.
3、一般可用于多个扩展函数链式调用
4、数据model多层级包裹判空处理的问题
also fun T.also(block: (T) -> Unit): T { block(this); return this } it指代当前对象 返回this 适用于let函数的任何场景,一般可用于多个扩展函数链式调用

欢迎关注Kotlin开发者联盟,这里有最新Kotlin技术文章,每周会不定期翻译一篇Kotlin国外技术文章。如果你也喜欢Kotlin,欢迎加入我们~~~

Kotlin系列文章,欢迎查看:

Kotlin邂逅设计模式系列:

  • 当Kotlin完美邂逅设计模式之单例模式(一)

数据结构与算法系列:

  • 每周一算法之二分查找(Kotlin描述)

翻译系列:

  • [译] Kotlin中关于Companion Object的那些事
  • [译]记一次Kotlin官方文档翻译的PR(内联类)
  • [译]Kotlin中内联类的自动装箱和高性能探索(二)
  • [译]Kotlin中内联类(inline class)完全解析(一)
  • [译]Kotlin的独门秘籍Reified实化类型参数(上篇)
  • [译]Kotlin泛型中何时该用类型形参约束?
  • [译] 一个简单方式教你记住Kotlin的形参和实参
  • [译]Kotlin中是应该定义函数还是定义属性?
  • [译]如何在你的Kotlin代码中移除所有的!!(非空断言)
  • [译]掌握Kotlin中的标准库函数: run、with、let、also和apply
  • [译]有关Kotlin类型别名(typealias)你需要知道的一切
  • [译]Kotlin中是应该使用序列(Sequences)还是集合(Lists)?
  • [译]Kotlin中的龟(List)兔(Sequence)赛跑

原创系列:

  • 教你如何完全解析Kotlin中的类型系统
  • 如何让你的回调更具Kotlin风味
  • Jetbrains开发者日见闻(三)之Kotlin1.3新特性(inline class篇)
  • JetBrains开发者日见闻(二)之Kotlin1.3的新特性(Contract契约与协程篇)
  • JetBrains开发者日见闻(一)之Kotlin/Native 尝鲜篇
  • 教你如何攻克Kotlin中泛型型变的难点(实践篇)
  • 教你如何攻克Kotlin中泛型型变的难点(下篇)
  • 教你如何攻克Kotlin中泛型型变的难点(上篇)
  • Kotlin的独门秘籍Reified实化类型参数(下篇)
  • 有关Kotlin属性代理你需要知道的一切
  • 浅谈Kotlin中的Sequences源码解析
  • 浅谈Kotlin中集合和函数式API完全解析-上篇
  • 浅谈Kotlin语法篇之lambda编译成字节码过程完全解析
  • 浅谈Kotlin语法篇之Lambda表达式完全解析
  • 浅谈Kotlin语法篇之扩展函数
  • 浅谈Kotlin语法篇之顶层函数、中缀调用、解构声明
  • 浅谈Kotlin语法篇之如何让函数更好地调用
  • 浅谈Kotlin语法篇之变量和常量
  • 浅谈Kotlin语法篇之基础语法

Effective Kotlin翻译系列

  • [译]Effective Kotlin系列之考虑使用原始类型的数组优化性能(五)
  • [译]Effective Kotlin系列之使用Sequence来优化集合的操作(四)
  • [译]Effective Kotlin系列之探索高阶函数中inline修饰符(三)
  • [译]Effective Kotlin系列之遇到多个构造器参数要考虑使用构建器(二)
  • [译]Effective Kotlin系列之考虑使用静态工厂方法替代构造器(一)

实战系列:

  • 用Kotlin撸一个图片压缩插件ImageSlimming-导学篇(一)
  • 用Kotlin撸一个图片压缩插件-插件基础篇(二)
  • 用Kotlin撸一个图片压缩插件-实战篇(三)
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用

Kotlin系列之let、with、run、apply、also函数的使用相关推荐

  1. Kotlin let、with、run、apply、also函数的使用

    let,with,run,apply,also 是内联扩展函数 下面是自己使用的心的如果有错的地方希望给予指正谢谢 这几个主要用来简化操作,使得代码可读性提高 ,下面列举项目中使用效果 1 let 先 ...

  2. Kotlin let with run apply also filter if常规用法笔记

    仅供个人学习参考 let mVideoPlayer?.let {it.setVideoView(activity.course_video_view)it.setControllerView(acti ...

  3. 轻松理解kotlin中标准函数let、run、with、apply、also的区别

    kotlin中,let.run.with.apply.also这几个标准函数使用起来非常方便,合理使用可以很大程度上的简化代码,但是这几个标准函数用法和功能相似,初学kotlin时,很容易会不知道该选 ...

  4. [译]Effective Kotlin系列之探索高阶函数中inline修饰符(三)

    简述: 不知道是否有小伙伴还记得我们之前的Effective Kotlin翻译系列,之前一直忙于赶时髦研究Kotlin 1.3中的新特性.把此系列耽搁了,赶完时髦了还是得踏实探究本质和基础,从今天开始 ...

  5. Kotlin系列之集合和函数式API完全解析-上篇

    简述: 今天带来的是Kotlin浅谈系列的第八讲,这讲我们一起来聊聊Kotlin这门语言对函数式编程的支持.我们都知道在kotlin这门语言中函数荣升成为了一等公民,所以在支持函数式编程的方面,Kot ...

  6. Kotlin系列之基础语法

    标签: Kotlin      Kotlin基础语法 目录: 一.包的声明 二.函数的定义 三.常量和变量 四.注释 五.字符串模板 六.使用条件表达式 七.NULL检查机制 八.类型检测以及自动类型 ...

  7. Kotlin系列之Lambda编译成字节码过程完全解析

    简述: 今天带来的是Kotlin浅谈系列第七弹,上篇博客我们聊到关于Kotlin中的lambda表达式的一些语法规则和基本使用.然而我们并没有聊到Kotlin的lambda表达式的本质是什么?我们都知 ...

  8. [译]Effective Kotlin系列之考虑使用原始类型的数组优化性能(五)

    翻译说明: 原标题: Effective Kotlin: Consider Arrays with primitives for performance critical processing 原文地 ...

  9. Kotlin系列之Lambda表达式(1)

    今天开始后续的几篇Kotlin的文章会介绍Kotlin中Lambda表达式相关的内容. 什么是Lambda表达式 在Java8中引入了Lambda表达式,这是最令Java开发者激动和期待的一个功能.那 ...

最新文章

  1. matlab 类 方法,最好的组织MATLAB类的方法?
  2. 计算机作曲 研究生 武汉音乐学院,计算机音乐作曲(武汉音乐学院2021年招收攻读硕士学位研究生考试大纲)...
  3. 2014 Red Hat Summit(红帽峰会)PPT合集
  4. 怎么查看表用了那个序列_3套阴瑜伽序列,让你身心平衡
  5. xp升级windows7_微软大升级!Windows系统电脑将告别杀毒软件
  6. bzoj 1681: [Usaco2005 Mar]Checking an Alibi 不在场的证明(BFS)
  7. 头文件循环包含,导致找不到定义的类
  8. 做完c语言通讯录系统后的小结,c语言通讯录管理系统的总结
  9. PPT素材模板哪个网站资源内容比较丰富?
  10. 兄弟连php课程,LAMP兄弟连PHP课程学习笔记 第一天 PHP基本语法
  11. Qt5给Excel添加批注
  12. Win10 笔记本 共享 wifi 热点
  13. 内核block层IO调度器—bfq算法深入探索2
  14. 非线性控制1.3——SPR条件、Schur补引理
  15. IO多路复用和epoll反应堆
  16. 初识linux之进程
  17. 等保2.0:这些等保测评要求,你都知道吗?
  18. 深入剖析NVMe Over Fabrics
  19. 【分享】关闭科学上网后网络连接故障
  20. 图像处理之高斯金字塔

热门文章

  1. 文章付费阅读系统(含小程序)
  2. 输出调节基本概念1.1——有界性与稳定性的定义及判断
  3. Opencv 光流法解析
  4. 最受欢迎的有养生养颜功效的15种零食
  5. GIS应用技巧之泰森多边形分析
  6. MathJax 基本操作
  7. 计算机职业编个谜语,职业编成谜语
  8. Java开发 - 不知道算不算详细的分布式事务详解
  9. Java快速开发平台,JEECG 3.7.5 Vue SPA单页面应用版本发布
  10. 思念是一种病(转载)