五,Kotlin-函數(shù)進(jìn)階

1,高階函數(shù)

定義
  • 函數(shù)的參數(shù)類型包含函數(shù)或者返回值為函數(shù)類型
  • 如果函數(shù)的參數(shù)只有一個參數(shù)或者最后一個參數(shù)為一個lambda表達(dá)式則可以把該函數(shù)提取到"()"外部,如果該函數(shù)沒有其他的參數(shù)可以把"()"省略

如下例中三個形式不同,意義相同

IntArray(5) {
        it
    }.forEach({ele: Int ->
        ele + 1
    })
    
    IntArray(5) {
        it
    }.forEach(){ele: Int ->
        ele + 1
    }
    
    IntArray(5) {
        it
    }.forEach{ ele: Int ->
        ele + 1
    }
這里有關(guān)lambda表達(dá)式有幾點需要注意

lambda表達(dá)式的類型推導(dǎo)有個順序,實際上lambda最后一行不管表達(dá)式的值的類型是什么,我們都可以把他的返回類型聲明為Unit,當(dāng)然你不聲明的話就像你說的是返回表達(dá)式的類型也就是Int了。這里foreach的參數(shù)類型是確定的,等于為參數(shù)的lambda表達(dá)式聲明了類型必須為int->unit。

val func = { ele: Int ->
        ele + 1
    }
這個func 相當(dāng)于已經(jīng)聲明了是 Int-> Int  所以不能再傳入foreach中,

     { ele: Int ->
        ele + 1
    }
沒有變量接收,所以可以被foreach聲明為int ->Unit

接下來寫個例子

1,寫個求函數(shù)運行時長的函數(shù)

fun cast(func: () -> Unit) {
    val start = System.currentTimeMillis();
    func()
    println(System.currentTimeMillis() - start)
}

2,寫一個求斐波那契數(shù)的函數(shù)

fun fibonacci(): () -> Long {
    var first = 0L
    var second = 1L
    return {
        val next = first + second
        val current = first
        first = second
        second = next
        current
    }
}

3,求出得到前10個斐波那契數(shù)所需時間

cast {
        val funcc= fibonacci()
        for (i in 1..10){
            println(funcc())
        }
    }

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

定義

  • 內(nèi)聯(lián)函數(shù):就是把函數(shù)的參數(shù)直接挪到函數(shù)體內(nèi)進(jìn)行執(zhí)行
  • 一個普通的函數(shù) 加上"inline" 即可定義為內(nèi)聯(lián)函數(shù)
  • 高階函數(shù)更適合做內(nèi)聯(lián)函數(shù),因為本身高階函數(shù)會再次調(diào)用或返回函數(shù),
  • 好處,減少了函數(shù)的調(diào)用,節(jié)省性能的開銷
fun main() {
    castTime {
        println("HelloWord")
    }
}

inline fun castTime(func: () -> Unit) {
    val startTime = System.currentTimeMillis()
    func()
    // 編譯器在編譯時相當(dāng)于把函數(shù)體挪到這個位置
    //println("HelloWord")
    println(System.currentTimeMillis() - startTime)
}
高階函數(shù)的內(nèi)聯(lián)
  • 函數(shù)本身被內(nèi)聯(lián)到調(diào)用處
  • 函數(shù)的函數(shù)參數(shù)被內(nèi)聯(lián)到調(diào)用處
內(nèi)聯(lián)高階函數(shù)的return

示例

val arrays = arrayOf(1, 2, 3, 4)
    arrays.forEach {
        if (it == 3) return@forEach
        println("---------$it---------")
    }

結(jié)果

---------1---------
---------2---------
---------4---------
內(nèi)聯(lián)函數(shù)無法像Java中那樣通過break結(jié)束本次遍歷,只能通過標(biāo)簽,跳出本次內(nèi)聯(lián)函數(shù)的調(diào)用

在lambda表達(dá)式中直接return的寫法也叫l(wèi)ocal return,與之相對應(yīng)的 如果在lambda表達(dá)式外部調(diào)用的 叫 non-local return ;
其實簡單理解就是帶內(nèi)聯(lián)函數(shù)標(biāo)簽的中斷叫l(wèi)ocalreturn ;不帶內(nèi)聯(lián)函數(shù)標(biāo)簽的中斷叫non-local return

non-local return 返回的是主調(diào)用函數(shù),直接中斷內(nèi)聯(lián)函數(shù)的調(diào)用

例子:該次return 直接返回到main函數(shù),之后的也不再執(zhí)行

fun main() {
    val arrays = arrayOf(1, 2, 3, 4)
    arrays.forEach {
        if (it == 3) return
        println("---------$it---------")
    }
 println("------------------")
}
并不是所以有得內(nèi)聯(lián)函數(shù)都可以進(jìn)行non-local return
crossline關(guān)鍵字

例如:由于block定義和調(diào)用處于不同的上下文,此時編譯器不會通過,除非加上crossline 關(guān)鍵字表示 block 肯定不會出現(xiàn)non-local return,這樣才行

inline  fun runnable(crossinline block:()->Unit):Runnable{
    return object :Runnable{
        override fun run(){
            block()
        }
    }
}
內(nèi)聯(lián)屬性
內(nèi)聯(lián)函數(shù)的限制

3,幾個有用的函數(shù)

具體用法:

class Person(var name: String, var age: Int, var sex: Boolean) {
}

fun main() {
    val person = Person("WJF", 25, true)
    println("${person.name}-${person.age}-${person.sex}")
    person.let {
        it.name = "11111"
        it.age = 1
        it.sex = false
    }
    person.run {
        name = "22222"
        age = 2
        sex = false
    }
    println("${person.name}-${person.age}-${person.sex}")

    var person2 = person.also {
        it.name = "33333"
        it.age = 3
        it.sex = true
    }
    println("${person.name}-${person.age}-${person.sex}")
    var person3 = person.apply {
        name = "44444"
        age = 4
        sex = false
    }
    println("${person.name}-${person.age}-${person.sex}")

    println("${person2 == person3}")
    println("${person2 === person3}")
}

運行結(jié)果

WJF-25-true
22222-2-false
33333-3-true
44444-4-false
true
true

結(jié)果我們得出結(jié)論

  • 可以看出其實let()和run()只有內(nèi)部引用不同,同樣都沒有返回值
  • also()和apply()只有內(nèi)部引用不同,同時返回了Person對象,但是這個對象都是原始的person 只是屬性變了引用沒變
user 的使用

將讀出build.gradle文件的每一行內(nèi)容并打印出來

File("build.gradle").inputStream().reader().buffered()
        .use {
            val readLines = it.readLines()
            readLines.forEach { ele: String ->
                println(ele)
            }
        }

4,集合變換與序列

<1>集合的變換
  • filter :保留滿足條件的元素
  • map :集合中的所有元素一 一映射到其他元素結(jié)構(gòu)新集合
  • floatMap : 集合中所有的元素一 一映射到新集合并合并這些集合得到新的集合

個人理解

1,filter就是過濾元素形成新的集合
2,map就是針對當(dāng)前集合對其元素進(jìn)行變換,之后組成新的集合
3,floatMap 就是對集合中的每個元素進(jìn)行操作形成n個新的集合 并且將他們組合在一起

舉例

fun main() {
    val arr = arrayOf(1, 2, 3, 4, 5)

    arr.asSequence()
        .filter {
            println("filter${it}")
            it % 2 == 0
        }.forEach {
            println("$it")
        }
    //filter就是過濾元素
    arr.filter {
        println("filter${it}")
        it % 2 == 0
    }.forEach {
        println("$it")
    }
    //map就是針對當(dāng)前集合對其元素進(jìn)行變換,形成新的集合
    arr.map {
        "$it"
    }.forEach {
        println(it)
    }
    //floatMap 就是對集合中的每個元素進(jìn)行操作形成n個新的集合  并且將他們組合在一起
    arr.flatMap {
        (1..it)
    }.forEach {
        println(it)
    }
//    arr.asSequence()
//        .flatMap {
//            (1..it).asSequence()
//        }.forEach {
//            println(it)
//        }

}
asSequence() 是數(shù)據(jù)流的意思,相當(dāng)于數(shù)據(jù)流的"懶加載",像上個例子中,如果加上asSequence()在沒有出口(例如forEach)的時候是不會執(zhí)行的,而且執(zhí)行順序 變成集合中數(shù)據(jù)一個一個的網(wǎng)下執(zhí)行,不是統(tǒng)一變換后在執(zhí)行下一步

例如

val arr = arrayOf(1, 2, 3, 4, 5)
    arr.filter {
        println("filter${it}")
        it % 2 == 0
    }.forEach {
        println("$it")
    }

執(zhí)行結(jié)果為

filter1
filter2
filter3
filter4
filter5
2
4

在加上.asSequence()之后

 val arr = arrayOf(1, 2, 3, 4, 5)
    arr.asSequence()
        .filter {
            println("filter${it}")
            it % 2 == 0
        }.forEach {
            println("$it")
        }

執(zhí)行結(jié)果為

filter1
filter2
2
filter3
filter4
4
filter5
<2>集合的聚合操作

聚合操作其實就需要將集合里的元素進(jìn)行運算

  • sum : 集合所有元素求和
  • reduce : 將元素依次按規(guī)則聚合,結(jié)果與元素類型一致
  • fold : 給定初始化值,將元素按規(guī)則聚合,結(jié)果與初始化值類型相同

例子

val arr = arrayOf(1, 2, 3, 4)
    val arrStr = arrayOf("11", "21", "31", "14")
    //sum操作符
    println(arr.sum())
    //reduce操作符
    val count = arr.reduce { acc, i ->
        acc + i
    }
    println(count)
    //fold操作
    arr.fold(StringBuilder()) { acc, i ->
        acc.append(i)
    }.forEach {
        println(it)
    }
    //fold操作
    arr.fold("") { acc, i ->
        "$acc$i"
    }.forEach {
        println(it)
    }
    //zip變換
    arr.zip(arrStr).forEach {
        println("${it.first}--${it.second}")
    }

5,SAM轉(zhuǎn)換

Java的lambda表達(dá)式?jīng)]有自己的類型,必須有一個接口來接收它,而且這個接口必須是單一方法,實際上Java中的lambda的類型就是"單一方法的接口類型" ,也可以是Kotlin的接口進(jìn)行接收

Kotlin中的匿名內(nèi)部類

寫法

object:接口名稱{
     方法(){
          xxxxxx
      }
}

對于Kotlin調(diào)用Java的匿名內(nèi)部類時,如果寫成lambda形式,Kotlin自動會把lambda轉(zhuǎn)換成匿名內(nèi)部類的形式

坑!!!!!!!!!!!

對于Kotlin調(diào)用Java代碼時有些需要添加和移除listener的,不能再用lambda表達(dá)式了,因為移除的不是同一個對象

正確操作

定義一個Java類

public class EventManager {

    private HashSet<OnEventListener> hashSet=new HashSet<>();
    public interface OnEventListener {
        public void onEvent(int i);
    }
    public void addEventListener(OnEventListener eventListener){
        hashSet.add(eventListener);
    }
    public void removeEventListener(OnEventListener eventListener){
        hashSet.remove(eventListener);
    }
}

正確調(diào)用

val manager=EventManager()
    //使用匿名內(nèi)部類的形式先定義出具體對象
    val onEvent=EventManager.OnEventListener {
        println(it)
    }
    //添加該對象
    manager.addEventListener(onEvent)
    //移除該對象
    manager.removeEventListener(onEvent)

小案例:讀取項目的build.gradle文件的每個字符出現(xiàn)的次數(shù)

//讀取build.gradle文件各個字符出現(xiàn)的次數(shù)
    File("build.gradle")
        .readText()//文件到String
        .toCharArray()//變成字符數(shù)組
        .filter { !it.isWhitespace() }//過濾空白字符
        .groupBy { it }//以每個字符為分組依據(jù)進(jìn)行分組
        .map {
            it.key to it.value.size//拿到每個字符對應(yīng)的size
        }.let {
            println(it)//輸出
        }

6,DSL

?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

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