1. 程式人生 > 程式設計 >擁抱kotlin之如何習慣使用kotlin高階函式

擁抱kotlin之如何習慣使用kotlin高階函式

前言

kotlin提供了高階函式這個概念,可以在一些場景提高編碼效率

一、什麼是高階函式

通俗的說和數學裡面的高階函式概念類似,也就是函式裡面的引數可以是函式。當然返回值也可以是函式。

二、kotlin高階函式使用場景分析

1.先看看平時使用比較多的內建高階函式

用kotlin寫view的onClickListener

 tV.setOnClickListener {
   //doSomeThing
  }

裡面的lamba表示式就是一個函式

不太形象?再看看集合裡面的filter、map

listOf(1,2,3)
   .filter { it > 2 }
   .map { it + 5 }

/**
 * Returns a list containing only elements matching the given [predicate].
 */
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
 return filterTo(ArrayList<T>(),predicate)
}

filter、map的引數都是一個lambda函式

2.高階函式有什麼用

就拿filter函式來說,比如實現一個過濾的邏輯,判斷是符合的

若classA 和classB都需要呼叫這個函式,那麼函式就需要相容這兩種情況

fun filter(): Boolean {
  if (classA) {
   return true
  } else if (classB) {
   return false
  }
  return false
 }

if else無可厚非,但是如果後面有classC classD...都需要考慮呢,這顯然違背了開閉原則。那麼自然是要面向抽象而不是具體,當然就是抽象類或者介面。

若用java的方式去實現,會變成這樣

interface IJudge {
  fun canFilter(): Boolean
 }

 class ClassA : IJudge {
  override fun canFilter(): Boolean {
   return true
  }
 }

 class ClassB : IJudge {
  override fun canFilter(): Boolean {
   return false
  }
 }

 fun filter(a:Int,b:Int,jugde: IJudge): Boolean {
  //加一些邏輯
  return jugde.canFilter()
 }

這個是硬傷,面向抽象就得加這麼介面,然後多寫一些程式碼。

若用高階函式實現

  fun filter(a: Int,b: Int,canFilter: (a:Int,b:Int) -> Boolean): Boolean {
  //加一些邏輯
  return canFilter(a,b)
 }
  //呼叫方1
  filter(1,2) { a: Int,b: Int ->
   a * b > 10
  }
  //呼叫方2
  filter(1,b: Int ->
   a + b < 5
  }

這樣就省了個介面,後面分析實際是編譯器幫忙處理,其實還是生成了介面

三、kotlin高階函式的實現

來看看kotlin編譯器是怎麼實現的吧

首先把上面那段kotlin程式碼反編譯成java

kt:
  fun filter(a: Int,b:Int) -> Boolean): Boolean {
    //加一些邏輯
    return canFilter(a,b)
  }
java:
 public final boolean filter(int a,int b,@NotNull Function2 canFilter) {
   Intrinsics.checkParameterIsNotNull(canFilter,"canFilter");
   canFilter.invoke(a,b);
   return (Boolean)canFilter.invoke(a,b);
  }

實際上是kt內建的 Functions.kt

這裡由於我傳的是2個引數的lambda函式,所以呼叫的是Function2

那麼從這裡能得來上面結論:

a.高階函式所謂的可以省略介面,其實只能省略只有一個方法的介面,因為function函式只有一個方法

b.上邊的fliter函式除了canFIlter(a,b)還可以使用canFilter.invoke(a,b)呼叫。這個在需要對函式判空的時候很有用。比如替換隻有一個方法的介面回撥可以callback?.invoke(a,b,c) , 因為callbck?(a,c)是不能編譯通過的。

c.雖然Functions.kt檔案方法數是有限的,感覺意味著lambda引數是有限的,最多22個引數,超過會編譯失敗。但是當真的超過時,會呼叫另外一個FunctionN.kt

operator fun invoke(vararg args: Any?): R

不過如果誰寫的函式,直接傳參20多個還不封成物件或者builder,怕是腿都要被打斷.......

四、關於高階函式替換介面的討論

上面已經討論了,當介面只有一個方法時,確實可以用高階函式代替,省略一個介面。

但是當介面有多個方法時,顯然不能直接替換。雖然也可以把幾個函式包裝在一起使用,但是還是感覺多此一舉。
多人並行開發的時候,比如一個人負責寫一個負責ui,一個負責使用ui處理業務邏輯。先把介面定好,介面方法文件寫好,一目瞭然。這一方面還是介面好很多,當只有簡單的一個方法時,用高階函式要方便一些。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對我們的支援。