距離上篇Kotlin文章,應該有差不多半年時間.沒別的原因,因為,懶,而且,想產出一篇稍微質量好的部落格好難. 最近在研究Python,所以最近也可能會更新一些Python的學習筆記.
StandardKt.kotlin原始碼位於kotlin-stdlib-common/kotlin包下; Kotlin的擴充套件函式有多爽,我應該不用再描述了吧~~; 如果對kotlin擴充套件函式概念不清的可以找找我前面的文章;
Kotlin提供了一個標準函式庫,開發過程中使用十分頻繁,也十分方便,只能說我已經徹底無法適應java開發了= =.
下面我們來探究一下這個標準庫的原始碼.標準庫函式容易混淆,但原始碼比較簡單,不要慌.
標準庫的函式基本都是行內函數 inline修飾.不知道啥是行內函數的,翻翻我前面的文章吧.
1.run函式
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
複製程式碼
run函式在標準庫有兩個; 我們直接看第二個擴充套件函式.入參是一個block函式,型別是一個T的擴充套件函式T.()->R,無參,返回值是R型別 ,也可以傳入lambda表示式,記住,kotlin中萬物皆物件,所以函式也是個物件; 例子:
/**
* 取絕對值
*/
fun Int.pow2():Double{
return Math.pow(toDouble(),2.toDouble())
}
fun testFun(){
val pow2 = "I am Zen".length.run({pow2()}) //1
Log.d(TAG,"pow2:$pow2")
}
執行testFun(),輸出:pow2:64.0
在Kotlin中,如果引數是最後一個引數,{}可以提到()之後,那麼上述1式子可以演變==>
val pow2 = "I am Zen".length.run(){pow2()}
Koltin中又規定,如果引數是唯一一個引數,那麼()也可以省略,1式子可以演變==>
val pow2 = "I am Zen".length.run{pow2()}
同樣的,如果直接傳入lamda表示式,也是ok的,1式可以這樣寫==>
val pow2 = "I am Zen".length.run{Math.pow(toDouble(),2.toDouble())}
複製程式碼
2.let函式
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
複製程式碼
同學們,仔細觀察,let函式和run函式的區別 可以看到,T.let 入參傳入的block函式,其引數是T,返回值依舊是R,block在內部呼叫時傳入T.this;
/**
* 取絕對值
*/
fun Int.pow2():Double{
return Math.pow(this.toDouble(),2.toDouble())
}
fun testFun(){
val pow2 = "I am Zen".length.let { Math.pow(it.toDouble(),2.toDouble())} //1
Log.d(TAG,"pow2:$pow2")
}
執行testFun(),輸出:pow2:64.0
仔細觀察這個例子,可以看到它和run函式的區別在於,run傳入的block函式內部直接持有的是T的引用;而let是外部傳入的T的引用,在lamda中用預設用it表示;
複製程式碼
3.with函式
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
複製程式碼
with函式和上述兩個又很像,= =,我們來仔細辨別一下,它又有什麼不同
fun testFun(){
val pow2 = with("I am Zen".length){
Math.pow(toDouble(),2.toDouble())
}
Log.d(TAG,"pow2:$pow2")
}
執行testFun(),輸出:pow2:64.0
with函式有兩個入參receiver:T,block函式,關於T的擴充套件函式,返回R
複製程式碼
4.also函式
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
複製程式碼
also函式和let函式的區別在於,also返回的自身,入參block函式,無返回值
val also = "I am Zen".length.also {
Math.pow(it.toDouble(), 2.toDouble())
}
Log.d(TAG,"also:$also")
輸出:also:8
字串"I am Zen".length為8,對8執行also函式,內部持有外部傳入的T引用,用it表示,內部計算了it的二次冪,但是返回的還是T本身,也即是8;所以,不難理解吧;和also函式類似的是apply函式.
複製程式碼
5.apply
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
複製程式碼
apply函式十分常用,它可以輕鬆實現java的鏈式呼叫,我們不用再那麼麻煩寫build模式啦; 可以看出apply函式和also函式很相識,不同的是,對於lamda內部,apply函式中直接持有T的引用,this可以省略,所以可以直接呼叫關於T的所有api,而also持有的是外部傳入的T的引用,用it表示,所以需要用it來呼叫關於T的所有api
6.takeIf,takeUnless
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (predicate(this)) this else null
}
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (!predicate(this)) this else null
}
複製程式碼
takeIf和takeUnless只是斷言相反 所以只講一下takeIf 這也是個十分實用的標準函式,傳入的predicate斷言函式,返回值Boolean,對於takeIf而言,符合條件則返回T,否則返回null,話不多說,直接看例子:
val age=17
val adultAge= age.takeIf {
it >= 18
}
Log.d(TAG,"isAdult or null:$adultAge")
輸出:isAdult or null:null
predicate函式斷言條件就是>=18;17執行takeIf,斷言失敗,返回null;
複製程式碼
7.repeat函式
@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0 until times) {
action(index)
}
}
複製程式碼
這個標準函式可以輕鬆實現迴圈任務;time是迴圈的次數,action函式指定具體迴圈的動作
repeat(5){
Log.d(TAG,"print:$it")
}
輸出:
print:0
print:1
print:2
print:3
print:4
複製程式碼
結尾
對於java基礎者,Koltin是一門十分容易上手的語言,而且在使用了將近一年的時間後,我深深感到Kotlin給我解放的生產力.