1. 程式人生 > >Standard.kt中的常用方法(let、with、apply等等)

Standard.kt中的常用方法(let、with、apply等等)

建立一個demo工程,使用Android Studio搜尋Standard.kt檔案,可以開啟Kotlin為我們準備的一系列提高程式設計速度,增加程式設計便捷性的方法

1、run方法 呼叫不需要有主語,可直接呼叫,傳入一個程式碼塊,該程式碼塊假設返回型別為R,run方法內部將直接執行程式碼塊內容,並將程式碼塊的返回值(R型別)直接作為run方法的返回值進行返回,注意:程式碼塊的最後一行是返回值

// run方法
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

// 使用示例
---------------------------------------------------------------------------------------------------------------------------------------
// a、展開寫法
// 1、定義一個變數a它是一個fun型別,即a是一個函式,該函式返回值為Int型別,函式體內最後返回3
// 2、執行Kotlin提供的run方法,並將a作為引數值傳入,run方法執行(內部是在執行a函式)後,將a函式執行結果(3)返回給result接收
// 3、可以理解為run方法只是一個殼

var a = fun(): Int {
    // somethings you want to do
    return 3
}
var result = run(a)
println("result=$result")

---------------------------------------------------------------------------------------------------------------------------------------
// b、簡便寫法
// 1、無需定義變數,無需定義函式,直接執行,一步到位

var result  = run {
    // somethings you want to do
    3
}
println("result=$result")

2、T.run方法 泛型T(任意合法型別)作為呼叫主語,傳入引數為T型別的擴充套件方法(注意,Kotlin中當引數列表的最後一個引數為函式型別時,可以直接寫在小括號外面的大括號裡),該方法返回值型別為R,同時和上述run方法一樣,T.run方法也是直接將傳入的擴充套件方法的返回值作為自己的返回值進行返回

// T.run方法
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

// 使用示例
---------------------------------------------------------------------------------------------------------------------------------------
// 1、建立一個類Cat,裡面有speak方法
// 2、定義一個任意方法,返回值為Int型別
// 3、定義一個cat物件
// 4、讓cat執行run方法,則run方法內,有一個this可供使用(this即cat.run{...}此處的cat)

class Cat {
    var name = "xiaobai"
    fun speak() {
        println("喵喵喵~")
    }
}
var otherFun = fun(): Int {
    println("我是打醬油的fun")
    return 3
}
var cat: Cat = Cat()
var result = cat.run {
    // somethings you want to do
	// there is a 'this' in this scope, so you can run cat's speak() method directly
    speak()
    name = "xiaohei"
    // somethings you want to do
    otherFun() // otherFun()'s return value will be returned as run()'s return
}
println("result=$result")

3、with方法 呼叫不需要有主語,可直接呼叫,傳入一個T型別,和一個程式碼塊(注意,Kotlin中當引數列表的最後一個引數為函式型別時,可以直接寫在小括號外面的大括號裡),該程式碼塊假設返回型別為R,with方法內部將T作為呼叫主語去執行程式碼塊內容,並將程式碼塊的返回值(R型別)直接作為with方法的返回值進行返回

// 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()
}

// 使用示例
---------------------------------------------------------------------------------------------------------------------------------------
// 1、同T.run方法,定義一個cat物件
// 2、將cat作為with的第一個引數傳入
// 3、執行with方法,和T.run方法非常類似,裡面也有一個this可供使用(this即傳入的cat)

var cat: Cat = Cat()
var result = with(cat) {
    // somethings you want to do
	// there is a 'this' in this scope, so you can run cat's speak() method directly
    speak()
    name = "xiaohei"
    // somethings you want to do
    otherFun() // otherFun()'s return value will be returned as with()'s return
}
println("result=$result")

4、T.apply方法 泛型T型別作為呼叫主語,傳入一個沒有返回值的函式(Unit表示沒有返回值型別,同Java中void),apply執行完後,返回呼叫主語本身

// T.apply方法
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

// 使用示例
---------------------------------------------------------------------------------------------------------------------------------------
// 1、簡單說,就是使用apply進行一系列各種騷操作,然後返回呼叫者本身,但是,並不是我們想象的出淤泥而不染,而是apply裡對cat的各種操作都應用在了cat身上,最後apply執行完,返回那個cat物件的引用(引用當然不會變,但是cat的name已經變成了xiaohei),然後繼續呼叫此cat的speak方法

var cat: Cat = Cat()
println("result=${cat.name}")
cat.apply {
    name = "xiaohei"
}.speak()
println("result=${cat.name}")

5、T.also方法 和apply非常像,只是將T.()改為了(T),此時scope內不再有this,而是T作為一個引數傳入函式,因此變成了it(it為系統預設變數名)

// T.also方法
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

// 使用示例
---------------------------------------------------------------------------------------------------------------------------------------
var cat: Cat = Cat()
cat.also {
    name = "xiaohei"// 報錯
    this.name = "xiaohei"// 報錯
    it.name = "xiaohei"
}.speak()

6、T.let方法 無非就是把also方法的返回值由呼叫主語的T型別改為了R型別(R為傳入函式的返回值型別),這也很好理解,also或者apply的目的是要返回呼叫主語T,所以傳入的函式返回值型別為什麼都沒有用,執行完also方法,也拿不到傳入函式的返回值,所以乾脆Unit型別即可,而let方法不打算返回T,而是打算返回傳入函式的返回值,讓程式設計師可以拿到傳入函式的返回值進行後續操作,let的一個主要作用,就是提供了一個it可供使用(it即呼叫主語T物件)

@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

// 使用示例
---------------------------------------------------------------------------------------------------------------------------------------
// 1、let內執行一些操作(這些操作可能和cat有關,也可能無關,但是一般都有關係時使用let,即操作和返回值都和呼叫主語相關時,使用let),然後給出一個返回值3,結束

var result = cat.let { 
    it.name = "xiaohei"
    3
}

7、T.takeIf方法 泛型T型別作為呼叫主語,傳入一個函式,該函式返回值型別為Boolean型別,如果返回true,則takeIf方法返回呼叫主語本身;如果返回false,則takeIf方法返回null

@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
}

// 使用示例
---------------------------------------------------------------------------------------------------------------------------------------
var cat:Cat = Cat()
cat.name = "xiaohei"
var temp = cat.takeIf {
    it.name == "xiaohei"
}
println(temp == cat) // true
println(temp === cat) // true

var cat:Cat = Cat()
cat.name = "xiaohei"
var temp = cat.takeIf {
    it.name = "libai" // any code what you want to do
    it.name == "xiaohei" // 最後一行的執行結果是內部函式的返回值,不是takeIf的返回值,takeIf的返回值是cat或null,詳見takeIf方法
}
println(temp == cat) // false,takeIf 內部對name進行了修改,故內部函式返回false(it.name == "xiaohei"這句是false),故takeIf最終返回null,此時null和cat比較肯定為false
println(temp === cat) // false,同上

8、T.takeUnless方法 和takeIf方法正好相反,內部判斷條件返回false,則takeUnless返回呼叫主語本身,否則返回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
}

// 使用示例
---------------------------------------------------------------------------------------------------------------------------------------
大寫的**略**

9、repeat方法 沒有呼叫主語,傳入要執行的函式塊的計劃執行次數,和要執行的函式

@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
    contract { callsInPlace(action) }

    for (index in 0 until times) {
        action(index)
    }
}

// 使用示例
---------------------------------------------------------------------------------------------------------------------------------------
// 示例1

repeat(3){
    println(it) // it 是函式內預設變數,表示當前呼叫次數
    println("hello")
}
// 呼叫結果:
// 0
// hello
// 1
// hello
// 2
// hello

// 示例2

var aa = fun(ii: Int) {
    println("hello + $ii")
}
repeat(3, aa)
// 呼叫結果:
// hello + 0
// hello + 1
// hello + 2