kotlin 深入学习笔记(三)
Contents
本文主要介绍kotlin的几个特性如下:
- 默认参数
- 命名参数
- std自带函数
继续,如果把编程语言看做一款应用产品,产品的功能对应语言特性,那么大多数编程语言功能都还算齐全,该有的也都有了,剩下一些增增减减,发个版本迭代也是和应用开发类似。这个应用的目的是为了什么呢,为了实现OOP?为了实现函数式编程?
不对,这种思路是本末倒置。就像我们写诗是为了抒发情感,而不是为了拼凑五言绝句平平仄仄平的韵律格式。编程语言设计出来,不是为了实现某个设计哲学,而是要为了被程序员使用。
就像面对 NPE,程序员说:我们不想写if null 判断,语言设计师说:可是添加个 null 实现起来更简单啊,我们加上吧。
你猜,要是编程语言是一个应用产品,这样的产品经理会不会被用户打。
6. 默认参数
Java为了避免在一个方法中传入过多参数,往往有两个解决方法:
- 将参数用builder封装一下
- 写多个方法重载,每次重载设置最后一个参数为默认值
这里举个方法重载的例子:
下面代码是用Java写的一个路由跳转类的toUriAct方法封装。
public static void toUriAct(Context ctx, String uriStr) { |
重复代码多,可读性差。但是,在Java中没得选,只能这么写。
而在kotlin中,只需要写一个方法:
fun toUriAct(ctx: Context, uriStr: String, |
kotlin是这么实现默认参数的呢?
kotlin 会为带默认参数的方法生成一个bridge方法,bridge 方法中添加了一个新的int型的mask参数,其值默认为2n-1(n = args.length)。mask的二进制位对应n个参数位。
调用时,如果传参覆盖了默认值,则将参数位对应的mask位置0。
在bridge方法中会检测默认参数的参数位是否置0,否则使用默认值去调用原方法。
// $FF: synthetic method |
但是这种参数带默认值的方法只能在kotlin代码中调用,如果要在Java中调用,只要方法声明前加上@JvmOverloads
标注,kotlin就会自动生成上述的方法重载,和用Java写的一样。
7. 命名参数
举例说明:
fun reformat(str: String, |
调用形式改变如下:
reformat(str, true, true, false, '_') |
reformat(str, |
Android Studio 3.0 已经自带了 parameter name hints ,所以这条现在看上去算不上特别吸引人了 :)
8. Std自带函数
在kotlin的Standard.kt
中定义了一些顶层高阶函数。
TODO
、take**
的几个顾名思义的方法不提了。下面的run、with、apply、also、let
方法命名并不是一个好的实践(动词+名词),我们无法直接从名字上看出这些方法怎么用,怎么区分,只好read code 。
下面一一分析:
- let
/** |
- receiver的扩展函数
- 参数是一个block,block参数为receiver,block内部通过it引用receiver
- 返回block的返回值
- 常常和安全调用操作符
?.
配合使用,用来保证let代码块中的it是已经null check过的
demo
val layout = LayoutStyle() |
- apply
/** |
- receiver的扩展函数
- 参数是一个block,这个block本身也是receiver的扩展函数,block会在receiver的闭包内执行
- 最终返回receiver自身
- 常常被用来当做内置的builder,可以方便地实现chain式调用
demo
class LayoutStyle { |
- with
/** |
- with 不是扩展函数
- 参数是 receiver 和 block,这个block是receiver的扩展函数,block会在receiver的闭包内执行
- 返回block的返回值
- 当需要频繁access某个实例时,可以把他包在with代码块中,然后直接访问实例内部变量和方法。
demo
val layout = with(contextWrapper) { |
-
run
run 有两个版本,第一个简单的调用block,第二个带上了泛型信息,成为了扩展函数。
/** |
/** |
第一种使用场景不明。。。,这里只看第二种。
- receiver的扩展函数
- 参数是block,这个block是receiver的扩展函数,block会在receiver的闭包内执行
- 返回block的返回值
剩下的also
分析省略,总结成表格如下:
function | 是否是扩展函数 | block闭包环境 | 返回值 |
---|---|---|---|
let | 是 | it | block result |
run | 是 | this | block result |
also | 是 | it | receiver self |
apply | 是 | this | receiver self |
缺 | 否 | it | block result |
with | 否 | this | block result |
缺 | 否 | it | receiver self |
缺 | 否 | this | receiver self |
从上面的表看出,这种function按理应该有 2 3 8种。目前只提供了5种(是否是因为kotlin conf想不到更多的方法名了呢?)。
根据返回值来看:
also 和 apply 返回的 receiver self,方便chain式调用。
let 和 run 返回的是 block result ,可以获取block运行结果。
它们的区别是block内部引用receiver时:
let
和also
使用it,而run
和apply
使用this。
从这个角度上看let
(带有被动式的操作it)、apply
(主动的应用到this)的命名还是比较贴切的。
with
作为非扩展函数是单独一档(没有同伴)。with返回 block result,而且 block 使用 this 引用receiver。从主要用法来说——简化对实例内部地访问,命名也很贴切,而且和别的语言惯例也是统一的。
另外在 kotlin.io.Closeable.kt
中还定义了use
扩展函数,也比较有意思。
- use
/** |
use
功能上会自动处理资源关闭的逻辑。有点类似 Java 7 的 try-with-resources 特性。
use
和 let
在函数定义上相似,返回的是 block result,使用it引用receiver。区别是use
的receiver 必须是Closeable
的子类。
demo 可以看第一篇开头的例子:
fun AssetManager.fileToString(filename: String): String { |
参考:
Mastering Kotlin standard functions: run, with, let, also and apply
Author: deskid
Link: https://deskid.github.io/2017/07/07/kotlin-learn-notes-3/
License: 知识共享署名-非商业性使用 4.0 国际许可协议