在使用 Kotlin
进行开发时,咱们不可避免的须要使用到 Standard.kt
内置的高阶函数:markdown
对刚刚接触 Kotlin
开发的来讲,使用的过程当中不免会有些吃力,这里对 Standard.kt
中的标准函数作一些总结与使用概括。app
run()
方法存在两种:less
public inline fun <R> run(block: () -> R): R {} public inline fun <T, R> T.run(block: T.() -> R): R {} 复制代码
public inline fun <R> run(block: () -> R): R {} 复制代码
分析:ide
说明:但凡函数接收的是一个代码块时,使用的时候通常都建议使用 {}
来包含代码块中的逻辑,只有在一些特殊状况下能够参数 (::fun)
的形式进行简化函数
例如:测试
run { println(888) } val res = run { 2 + 3 } 复制代码
这没什么难度,这里我想要说的是:
但凡涉及到须要传递的代码块参数,均可以省略不传递,对于参数只是一个代码块的时候,能够直接用 ::fun
【方法】 的形式传递到 ()
中。
啥意思?简单来说,若是传递单代码块格式是 block: ()
这样的,咱们能够这么干:this
fun runDemo() { println("测试run方法") } //咱们能够这么干 run(::runDemo) 复制代码
也就是说代码块格式为block: ()
这种的,用 ()
设置的方法必须是不含有参数的,例如上面的 runDemo()
方法就没有参数。spa
public inline fun <T, R> T.run(block: T.() -> R): R {} 复制代码
分析:code
T
类型的 run
方法,传递的依然是一个代码块,T
的内部一个变量 或 方法等,返回的是 一个 R
类型val str = "hello" val len = str.run { length } 复制代码
上面例子,一个字符串 str
,咱们执行 str
的 run
方法,此时在 run
方法中,咱们能够调用 String
类中的一些方法,例如调用 length
返回的是一个 Int
类型结果。orm
这种在执行一个类的中多个方法的时候,而且要求返回一个结果的时候,使用这个run方法可以节省不少代码量。
一样的,对于方法传递的是一个代码块的函数而言,若是其传递的代码块格式是 block: T.()
这种,咱们可使用 ::fun
的形式传递到 ()
中,只是这个传递的方法要求必须含有一个参数传递。 说的很绕口,直接看代码:
val str = "hello" str.run(::println) //println函数 public actual inline fun println(message: Any?) { System.out.println(message) } 复制代码
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {} 复制代码
分析:
with()
方法接收一个类型为 T
的参数和一个代码块R
类型的结果T.run()
方法很相似,只是这里将 T
传递到了with()
方法当中val str = "hello" val ch = with(str) { get(0) } println(ch) //打印 h 复制代码
一样的,这里代码块格式是 block: T.()
这种,所以根据上面说的规则,咱们一样能够写成下面这样:
val ch2 = with(str, ::printWith) fun printWith(str: String): Char? { return if (str.isEmpty()) null else str[0] } 复制代码
什么场景下使用 with()
比较合适?下面代码中就很好的使用了 with()
方法简化了代码:
class Preference<T>(val context: Context, val name: String, val default: T, val prefName: String = "default") : ReadWriteProperty<Any?, T> { private val prefs by lazy { context.getSharedPreferences(prefName, Context.MODE_PRIVATE) } //注解消除警告 @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST") override fun getValue(thisRef: Any?, property: KProperty<*>): T { return when (default) { is String -> prefs.getString(name, default) is Int -> prefs.getInt(name, default) is Long -> prefs.getLong(name, default) is Float -> prefs.getFloat(name, default) else -> throw IllegalStateException("Unsupported data.") } as T } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { with(prefs.edit()) { when (value) { is String -> putString(name, value) is Int -> putInt(name, value) is Long -> putLong(name, value) is Float -> putFloat(name, value) else -> throw IllegalStateException("Unsupported data.") } }.apply() } } 复制代码
public inline fun <T> T.apply(block: T.() -> Unit): T {} 复制代码
分析:
T
类型中的方法,变量等,而后返回自身 T
block: T.()
,但凡看到 block: T.() ->
这种代码块,意味着在大括号 {}
中能够直接调用T内部的 API
而不须要在加上 T.
这种【实际上调用为 this.
,this.
一般省略】val str = "hello" str.apply { length } //能够省略 str. str.apply { this.length } //能够这样 //block: T.()格式代码块,所以一样能够这么写: str.apply(::println) 复制代码
实际开发中,一般配合判空 ?
一块使用,减小 if
判断,例以下面这样:
var str: String? = "hello" //一系列操做后。。。 str?.apply(::println) ?: println("结果为空") 复制代码
上面代码,若是字符串 str
不为空直接打印出来,若是为空则打印 结果为空
public inline fun <T> T.also(block: (T) -> Unit): T {} 复制代码
分析:
T
类型中的方法,变量等,而后返回自身 T
apply
方法相似,只是在大括号中执行 T
自身方法的时候,必需要加上 T. 不然没法调用 T
中的 API
,什么意思呢?看下面代码:val str = "hello" str.also { str.length } //str.必须加上,不然编译报错 str.also { it.length } //或者用 it. 复制代码
上面代码中 {}
中使用了 it
来代替 str
,其实咱们还能够手动指定名称:
//{}中的s表明的就是str str.also { s -> s.length } 复制代码
这就是also与apply的区别所在。
另外,须要注意的是also入参的代码块样式:block: (T)
,这种样式跟 block: T.()
同样,可使用 ::fun
的形式传递到 ()
中,只是这个传递的方法要求必须含有一个参数传递。
所以咱们能够这样操做:
str.also(::println)
复制代码
public inline fun <T, R> T.let(block: (T) -> R): R {} 复制代码
分析: let
方法与上面的 also
方法及其相似,只是 also
方法返回的结果是自身,而 let
方法是传递类型 T
返回另一个类型 R
形式,所以在用法上也很相似:
var str:String? = "hello" //...一堆逻辑执行后 val len = str?.let { it.length } str.let(::println) 复制代码
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (predicate(this)) this else null } 复制代码
分析:
T
作内部判断,根据判断结果返回 null
或者 T
自身C++
中的一元谓词:方法只含有一个参数,而且返回类型是Boolean类型true
则返回自身,不然返回 null
看下使用代码:
val str = "helloWorld" str.takeIf { str.contains("hello") }?.run(::println) 复制代码
上面代码{}中判断字符串是否包含 "hello"
,是则返回本身,不是则返回 null,所以可使用?来判断,若是不为null
,可使用前面说的 run()
方法进行简单打印操做。 一样的,由于接收的代码块是一个一元谓词形式,所以,若是想要使用 (::fun)
方式来替代 {}
,则对应的函数方法必须知足两个条件:
Boolean
类型val str = "helloWorld" str.takeIf(::printTakeIf)?.run(::println) fun printTakeIf(str: String): Boolean { return str.contains("hello") } 复制代码
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (!predicate(this)) this else null } 复制代码
分析:这个方法跟 takeIf()
方法相似,只是内部判断为false的时候返回自身T ,而 true
的时候返回 null
,所以不过多说明,使用参考 takeIf()
方法。
public inline fun repeat(times: Int, action: (Int) -> Unit) { contract { callsInPlace(action) } for (index in 0 until times) { action(index) } } 复制代码
分析:repeat
方法包含两个参数:
//打印从0 到 100 的值,次数用到了内部的index repeat(100) { print(it) } //有好比,单纯的打印helloworld 100 次,就没有用到index值 repeat(100){ println("helloworld") } 复制代码
注意看传递的代码块格式:action: (Int)
,这就说明了要想使用(::fun)形式简化{}部分,须要代码块知足一个条件:
Int
类型或者 Any
的参数repeat(100, ::print) repeat(100, ::printRepeat) fun printRepeat(int: Int) { print(int) } 复制代码
不论是 Kotlin
中内置的高阶函数,仍是咱们自定义的,其传入的代码块样式,无非如下几种:
block: () -> T
和 block: () -> 具体类型
(::fun)
形式简化时,要求传入的方法必须是无参数的,返回值类型若是是T则可为任意类型,不然返回的类型必需要跟这个代码块返回类型一致block: T.() -> R
和 block: T.() -> 具体类型
(::fun)
形式简化时,要求传入的方法必须包含一个T类型的参数,返回值类型若是是R则可为任意类型,不然返回的类型必需要跟这个代码块返回类型一致。例如 with
和 apply
这两个方法block: (T) -> R
和 block: (T) -> 具体类型
(::fun)
形式简化时,要求传入的方法必须包含一个T类型的参数,返回值类型若是是R则可为任意类型,不然返回的类型必需要跟这个代码块返回类型一致。例如 let
和 takeIf
这两个方法只有搞清楚上面这三种代码块格式及其用法,对应的其余的一些例如 Strings.kt
中的 filter
、takeWhile
、flatMap
等一系列高阶函数,都能快速掌握。