思想交融,Android中的函數式編程(2):什麼是函數式編程

前言

上一篇文章,我們經過DiffUtil來引出了函數式的那麼一點點內容。今天的文章,將會重點聊一聊函數式編程所能給咱們開發模式上的改變。編程

思想交融,Android中的函數式編程(1):DiffUtil體驗安全

1、什麼是函數式編程

解答這個問題,讓咱們直接上維基百科吧:多線程

函數式編程(functional programming),是一種編程範式,它將計算機運算視爲函數運算,而且避免使用程序狀態(也就是我們上篇文章提到的stateless)以及不可變對象(上篇文章提到的immutable)。其中,λ演算(lambda表達式)爲該語言最重要的基礎。並且,λ演算的函數能夠接受函數看成輸入(引數)和輸出(傳出值)(也就是Kotlin中所推崇的高階函數)。app

概念巴拉巴拉說了一大堆,總結一下也就是:框架

  • 代碼無狀態
  • 使用不可變對象
  • Lambda表達式和高階函數

接下來,我們一一展開這些內容:less

2、函數式編程的特色

2.一、無狀態(stateless)

這個特性講究的是:函數不維護任何狀態ide

咋理解呢?對於函數式編程來講,函數所作的,就是接受輸入,而後處理完返回輸出。重點在於,函數執行完畢前,內部的任何變量是永遠不會被函數外所改變的。也就是說,參與函數執行的變量永遠是局部變量!函數式編程

在代碼裏就是這個樣子:函數

// 非函數式,有狀態(Stateful)
// 這裏有個成員變量,調這個全局函數變量++,這裏面是有狀態的,這個狀態在外部。因此,若是是多線程的話,這裏面的代碼是不安全的。
class StatefulAddition{
    var result = 0
    fun addOneCount():Int{
        result++
        return result
    }
}

// 函數式,無狀態(Stateless)
class StatelessAddition{
    fun addOneCount(one :Int): Int{
        return one + 1
    }
}
複製代碼

感覺到兩者在編碼方式上的不一樣了吧。沒感覺到?那就好好感覺一會兒...post

這裏我們想想,函數式編程講究這樣的思想,有什麼好處呢?

咱們知道,由於狀態的存在,在並行執行時引起bug的機率是很是高的,畢竟多線程操做成員變量的行爲很常見(咱們可使用加鎖的方式去解決問題),稍不留神就會被坑。因此沒有狀態就沒有傷害(畢竟在這種思想下,代碼的執行不包括中間狀態的出現)。

2.二、不可變變量(immutable)

immutable這個概念須要好好感覺。這個概念正好印證了Kotlin中val關鍵字的設計。咱們千萬不要單純的把val和Java中final關鍵字「混爲一談」。(雖然好像它們就是一個東西)

val定義一個不可變的變量,也就是說這個變量一但被聲明將不能被再次賦值。這也就是immutable所推崇的意義(Kotlin中val就是immutable的具體實現)。

讓咱們上一段代碼,感覺一下:

// Mutable
// 類中的成員變量都是以var聲明的
data class MutableModel(var name: String)
data class ImmutableModel(val name: String)

val mutableModel = MutableModel("mdove")
val newNameMutaleModel = mutableModel.apply { name = "new mdove" }

// Immutable
// 類中的成員變量都是以val聲明的,意味着:想換個name,必須得從新new這個類
val immutableModel = ImmutableModel("mdove")
val newNameMutableModel1 = ImmutableModel("new mdove")
// data class 特有的複製變量的方式(就是一種快捷的new)
val newNameMutableModel2 = immutableModel.copy("new mdove")
複製代碼

OK,這就是immutable。還記不記得,咱們在上篇文章中提到過:

由於DiffUtil設計自己就是對不一樣的集合對象進行diff。所以咱們在update的時候,就必需要輸入倆個不一樣的集合實例。

因此Immutable這個特性,完美的對應了Diff的特性。因此Immutable的特性在不少框架或者業務場景中很是很是的合適以及好用。這裏先有一個關於此的印象,而後在隨後的業務中慢慢去感覺,慢慢感覺~

2.三、高階函數

在函數式編程中引入了高階函數的概念,概念可能在Java中並非很常見。我們用一段代碼來感覺一下高階函數在Kotlin中的應用,以及它在Java中的能找到的相似實現。

// 高階函數
fun logString(mValue: String) {
    Log.d("mdove", mValue)
}

fun logList(list: MutableList<String>, func: (String) -> Unit) {
    list.forEach {
        func.invoke(it)
    }
}
// 直接把函數以參數的形式,傳進去
logList(mutableListOf("111","222","333"),::logString)


// 非高階函數
interface LogString {
    fun invoke(string: String)
}

fun logList(list: MutableList<String>, logString: LogString) {
    list.forEach {
        logString.invoke(it)
    }
}
// 匿名內部類的方式執行
logList(mutableListOf("111", "222", "333"), object :LogString{
    override fun invoke(string: String) {
        Log.d("mdove", string)
    }
})
複製代碼

高階函數的出現,讓函數式編程變得異常的方便,極大的提升了咱們的開發效率。如今已經入手Kotlin的小夥伴,確定對Kotlin中「變化無窮」的操做符搞的大呼「真香」了吧?

2.四、操做符

由於高階函數的存在,Kotlin中存在不少效率爆炸的操做符,接下來讓咱們經過具體的操做符和Java中的實現進行對比。直接上代碼,從代碼中感覺一切~~~

遍歷一種類型的集合,而後轉換成另種類型的集合

// Java實現
fun listLongToString(list: List<Long>): List<String> {
    val result= mutableListOf<String>()
    for (i in list.indices){
        result.add(list[i].toString())
    }
    return result
}
// Kotlin實現,map操做符
fun List<Long>.listLongToString(): List<String> {
    return this.map { it.toString() }
} 
複製代碼

打平某種類型的集合

// Java實現
fun getAllNames(list: List<TestModel>): List<String> {
    val result = mutableListOf<String>()
    for (i in list.indices) {
        for (j in list[i].nameList.indices) {
            result.add(list[i].nameList[j])
        }
    }
    return result
}
// Kotlin,flatMap操做符
fun List<TestModel>.getAllNames(): List<String> {
    return this.flatMap { it.nameList }
}
複製代碼

過濾掉全部爲null的集合

// Java實現
fun filterNullOrEmpty(list:List<String?>):List<String>{
    val result = mutableListOf<String>()
    for (i in list.indices){
        if (!list[i].isNullOrEmpty()){
            result.add(list[i]!!)
        }
    }
    return result
}
// Kotlin,filter操做符
fun List<String?>.filterNullOrEmpty:List<String>{
    return this.filterNotNull().filterNot { it.isEmpty() }
}
複製代碼

最後整一個有趣的操做符:groupBy

listOf("123", "1", "2", "3", "12", "321", "21", "45", "1234", "5678").groupBy { it.length }.map {
    Log.d("mdove", "${it.key} - ${it.value}")
}
複製代碼

直接經過輸出結果,來感覺這個操做符的魅力吧:

2.五、表達式

最後想聊一聊表達式(Expression)這個特性,表達式隨處可見。val num = a + b;這裏的a + b就是一個表達式,它賦值給某個變量。這彷佛很正常,可是有「不正常」的:

讓咱們來感覺一下Kotlin中更爲強大的表達式用法:

// 比較倆個數的大小,並輸出大的。在Java中的實現能夠是這樣:
fun sMax(a:Int,b:Int):Int{
    var result=0
    if(a>b){
        result=a
    }else{
        result=b
    }
    return result
}
// 能夠在Kotlin中if-else是一個表達式,也就是說它能夠賦值給變量,所以在Kotlin中的實現是這樣的:
fun eMax(a:Int,b:Int):Int{
    return if(a>b) a else b
}
複製代碼

尾聲

關於函數式的概念,我想文章已經寫的足夠清楚了。而且其中關於Java和Kotlin的代碼對比,基本可以感覺到函數式所帶來的不同的編程體驗。

這裏並不是是推崇和安利函數式編程,更多的是一種融合。有些時候咱們的確須要看看「外面的世界」,感覺更大更有趣的編程世界~~

我是一個應屆生,最近和朋友們維護了一個公衆號,內容是咱們在從應屆生過渡到開發這一路所踩過的坑,以及咱們一步步學習的記錄,若是感興趣的朋友能夠關注一下,一同加油~

我的公衆號:鹹魚正翻身
相關文章
相關標籤/搜索