在使用 Kotlin
進行開發時,咱們不可避免的須要使用到 Standard.kt
內置的高階函數:app
對剛剛接觸 Kotlin
開發的來講,使用的過程當中不免會有些吃力,這裏對 Standard.kt
中的標準函數作一些總結與使用概括。less
run()
方法存在兩種:ide
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 {}
複製代碼
分析:函數
說明:但凡函數接收的是一個代碼塊時,使用的時候通常都建議使用 {}
來包含代碼塊中的邏輯,只有在一些特殊狀況下能夠參數 (::fun)
的形式進行簡化測試
例如:ui
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
類型結果。cdn
這種在執行一個類的中多個方法的時候,而且要求返回一個結果的時候,使用這個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
等一系列高階函數,都能快速掌握。