Kotlin的Android廣播、高階函數(shù)探究(三)

1、repeat函數(shù)
2、擴展函數(shù)
3、運算符重載 operator
4、動態(tài)注冊一個廣播
5、高階函數(shù)
6、內(nèi)聯(lián)函數(shù) inline
7、oninline、crossinline

  • 1、repeat函數(shù)

定義: repeat定義傳入int參數(shù),傳入幾。被包裹的內(nèi)容就循環(huán)幾次。

fun getRepeat(string: String) {
    val intRange = (1..20).random()
    val stringBuilder = StringBuilder()
    //定義 參數(shù)是幾就循環(huán)幾次
    repeat(intRange) {
        stringBuilder.append(string + "\n")
    }
    println(stringBuilder.toString())
}
  • 2、擴展函數(shù)

定義 :可以對對象進行擴展,擴展后可直接調(diào)用。舉個例子吧
假設(shè) 一個字符串 “ABC123xyz11zzz1!@#”,我們想知道它的字母數(shù)量。用代碼寫的話,可以通過isLetter來判定。
常用寫法:

fun main() {
    val sss = "ABC123xyz11zzz1!@#"
    val letterCount = getLetterCount(sss)
    println(sss)
}

fun getLetterCount(string: String): Int {
    var count = 0
    for (char in string) {
        if (char.isLetter()) {
            count++
        }
    }
    return count
}

我們知道sss是String類型, 接下來對String進行擴展。
擴展方法如下:

fun main() {
    val sss = "ABC123xyz11zzz1!@#".letterCount()
    println(sss)
}

fun String.letterCount(): Int {
    var count = 0
    for (char in this) {
        if (char.isLetter()) {
            count++
        }
    }
    return count
}

擴展之后不用傳入?yún)?shù)了,可直接通過擴展的對象類型去獲取該方法。this表示傳入的參數(shù)值

更深入的了解可以看“鴻洋”發(fā)的https://mp.weixin.qq.com/s/CSlQHKZRdhAChbPrXsKLXA

  • 3、運算符重載 operator

運算符重載使用的是operator關(guān)鍵字,只要在制定函數(shù)的前面加上operator關(guān)鍵字就可以實驗運算符重載的功能了。

先來看看語法糖表達式和實際調(diào)用函數(shù)對照表


語法糖表達式和實際調(diào)用函數(shù)對照表.png

比如說是contains函數(shù)可以這么寫:

    if ("hello".contains("he")) {
        println("111")
    }

因為語法糖表達式可以對上述進行簡寫:

    val b = "he" in "hello"
    println(b)

這兩種寫法的效果是一樣的。明白語法糖了嗎?

  • 接下來對(1、repeat)的方法進行運算符的重載
fun getRepeat(string: String) {
    val intRange = (1..20).random()
    val stringBuilder = StringBuilder()
    //定義 參數(shù)是幾就循環(huán)幾次
    repeat(intRange) {
        stringBuilder.append(string + "\n")
    }
    println(stringBuilder.toString())
}

repeat我們知道將傳入的字符重復(fù)n次,如果能把這個重復(fù)寫成 str * n來表示Str重復(fù)n次,這種語法相對來說就更舒適~

operator fun String.times(n: Int): String {
    val stringBuilder = StringBuilder()
    repeat(n) {
        stringBuilder.append(this)
    }
    return stringBuilder.toString()
}

val s = "hello" * (1..20).random()
    println(s)

這樣就跟最開始的寫法一樣,是不是更簡便呢。而a.times也表示了乘。

  • 4、動態(tài)注冊一個廣播

class RecevierActivity : AppCompatActivity() {

    private lateinit var myReceiver1: MyReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_recevier)

        ///Volumes/personal/androidsdk/platforms/android-28/data/broadcast_actions.txt
        val intentFilter = IntentFilter()
        intentFilter.addAction("android.intent.action.TIME_TICK")
        myReceiver1 = MyReceiver()
        registerReceiver(myReceiver1, intentFilter)

    }

    inner class MyReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            ToastUtils.show(this@RecevierActivity, "this receiver")
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(myReceiver1)
    }
}

廣播的類別形式跟Java還是一樣的, 書寫的方式如上。別的廣播就不寫了,大同小異。
我這里只是監(jiān)聽了一下 系統(tǒng)時間發(fā)生變化的 action,更多的action可以在sdk的platforms/android-28/data/broadcast_actions.txt文件里看到。
涉及到的一些版本差異,權(quán)限此處不過多講解。有興趣可自己看看

  • 5、高階函數(shù)

定義:如果一個函數(shù)接收另一個函數(shù)作為參數(shù),或者返回值的類型是另一個函數(shù),那么稱為高階函數(shù)。

(String , Int) -> Unit

fun example1(fune: (String, Int) -> Unit) {
    fune("hello", 1)
}

這就代表了一個簡單的高階函數(shù), fune這個函數(shù)作為參數(shù), 而Unit則代表的是無返回類型,就跟void一個意思。

  • 接下來更深入一些:
fun example(n1: Int, n2: Int, opear: (Int, Int) -> Int): Int {
    val opear1 = opear(n1, n2)
    return opear1
}

n1,n2傳入倆Int參數(shù) , 可以看到把n1n2給了opear這個方法,來操作執(zhí)行。opear的執(zhí)行是可以自定義的。 假設(shè)如下:

fun plus(i: Int, i1: Int): Int {
    return i + i1
}

fun minus(i: Int, i1: Int): Int {
    val i2 = i - i1
    return i2
}

顯而易見minus 和plus執(zhí)行的邏輯不一樣,我們傳入上述的方法里

    val n1 = 100
    val n2 = 50
    val gaoji = example(n1, n2, ::plus)
    println(gaoji)

可以發(fā)現(xiàn)這個執(zhí)行結(jié)果是可以自定義的,這就是高階函數(shù)。
更高級的寫法,定義個專屬StringBuilder的apply

fun StringBuilder.build(build: StringBuilder.() -> Unit): StringBuilder {
    build()
    return this
}

    val build = StringBuilder().build {
        append("this")
        append("this")
    }
    println(build.toString())

功能跟apply一樣,只不過沒有涉及到泛型的概念。

  • 也可以用lambda來寫:
    val n1 = 100
    val n2 = 50
    val example = example(n1, n2) { n1, n2 ->
        n1 + n2
    }
    println(example)
  • 6、內(nèi)聯(lián)函數(shù)

    val n1 = 100
    val n2 = 50
    val example = example(n1, n2) { n1, n2 ->
        n1 + n2
    }
    println(example)

我們都知道,Kotlin代碼最終還是要編譯成Java字節(jié)碼的,而Java中并沒有高階函數(shù)的概念。那究竟怎么做的呢?

上面的代碼最終會被轉(zhuǎn)換:
Lambda表達式會變成Function接口的匿名類實現(xiàn),然后在invoke()函數(shù)中調(diào)用n1 + n2的邏輯,并將結(jié)果返回。我們一直使用的Lambda表達式在底層被轉(zhuǎn)換成了匿名類的實現(xiàn)方式,這表明我們每調(diào)用一次Lambda表達式,都會創(chuàng)建一個新的匿名類實例,當(dāng)然也會造成額外的內(nèi)存和性能開銷。為了解決這個問題,Kotlin提供了內(nèi)聯(lián)函數(shù)的功能,可以把開銷完全消除~

inline fun example(n1: Int, n2: Int, opear: (Int, Int) -> Int): Int {
    val opear1 = opear(n1, n2)
    return opear1
}

Kotlin編譯器會將內(nèi)聯(lián)函數(shù)中的代碼在編譯的時候自動替換到調(diào)用它的地方,這樣也就不存在運行時的開銷了。 上述的方法加了內(nèi)聯(lián)后,在編譯時等于下面代碼

    val n1 = 100
    val n2 = 50
    val example = n1 + n2 
    println(example)

val example = n1 + n2

  • noline

如果一個高階函數(shù)接收了兩個或者更多的函數(shù)類型參數(shù),這時如果我們加上了inline的話,Kotlin會把所有引用lambda的都進行內(nèi)聯(lián),但如果我只是想內(nèi)聯(lián)其中的某一個Lambda呢。

inline fun printString(ss: String, bolck: (String) -> Unit, noinline bolck2: (String) -> Unit) {
}

加上noinline修飾就可以了~

內(nèi)聯(lián)和非內(nèi)聯(lián)除了上述之外,還有個重要的點就是內(nèi)聯(lián)是可以通過return來返回的,來非內(nèi)聯(lián)只能局部返回。
比如非內(nèi)聯(lián):

    val str = "main start"
    printString(str) { string ->
        println("lambda1111")
        return@printString
        println("lambda2222")
    }

//
fun printString(ss: String, bolck: (String) -> Unit) {
    println("printString111")
    bolck(ss)
    println("printString222")
}

log->
printString111
lambda1111
printString222

這里使用了@printString,用來表示局部返回。

內(nèi)聯(lián)函數(shù):

    val str = "main start"
    printString(str) { string ->
        println("lambda1111")
        return
        println("lambda2222")
    }

inline fun printString(ss: String, bolck: (String) -> Unit) {
    println("printString111")
    bolck(ss)
    println("printString222")
}

log->
printString111
lambda1111

對比非內(nèi)聯(lián)可以發(fā)現(xiàn),內(nèi)聯(lián)的可以直接使用return,并且返回出當(dāng)前函數(shù)

  • crossinline
    將高階函數(shù)聲明成內(nèi)聯(lián)函數(shù)來講是個好的編碼習(xí)慣,絕大部分都是可以的,但也存在例外:
fun runRunable(bolck: () -> Unit) {
    val runnable = Runnable {
        bolck()
    }
    runnable.run()
}

如果將上述代碼聲明成內(nèi)聯(lián)會報錯

inline fun runRunable(bolck: () -> Unit) {
    val runnable = Runnable {
        bolck()
    }
    runnable.run()
}

Can't inline 'bolck' here: it may contain non-local returns. Add 'crossinline' modifier to parameter declaration 'bolck'

這塊報錯的意思就是說,
Lambda表達式在編譯的時候會被轉(zhuǎn)換成匿名類的實現(xiàn)方式, 而上述代碼實際上 就是在匿名類中調(diào)用傳入的函數(shù)類型參數(shù)。 而內(nèi)聯(lián)所引用的Lambda表達式允許使用return關(guān)鍵字進行函數(shù)返回,但是由于我們是在匿名類中調(diào)用函數(shù)參數(shù),此時是不可能進行外層調(diào)用函數(shù)返回的,最多只能對匿名類中的函數(shù)調(diào)用進行返回,因此報錯。
也就是說,如果我們在高階函數(shù)中創(chuàng)建了另外的Lambda或者匿名類的實現(xiàn),并且在這些實現(xiàn)中調(diào)用函數(shù)類型參數(shù),此時再將高階函數(shù)聲明成內(nèi)聯(lián)函數(shù),就一定會提示錯誤。

這種情況下不是不能使用內(nèi)聯(lián)了,只需要在函數(shù)類型參數(shù)上修飾 crossinline就可以解決這個問題。

inline fun runRunable(crossinline bolck: () -> Unit) {
    val runnable = Runnable {
        bolck()
    }
    runnable.run()
}

crossinline的作用就像是一種保證,保證在內(nèi)聯(lián)函數(shù)的Lambda表達式中一定不會使用return關(guān)鍵字,這樣沖突就不存在了。~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容