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ù)對照表

比如說是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)鍵字,這樣沖突就不存在了。~