標籤:
原文標題:Unleash functional power on Android (I): Kotlin lambdas
原文連結:http://antonioleiva.com/operator-overloading-kotlin/
原文Antonio Leiva(http://antonioleiva.com/about/)
原文發布:2015-08-05
雖然Java 8中已包含一些函數式工具,且如你想象那樣Android開發人員還不能夠立即(或許甚至根本不能)使用這些工具,但是如果恰當地使用,函數式編程依然是十分強的工具。為此許多其他程式設計語言正開始進行解決這個問題。
現代程式設計語言的函數式編程
由於函數式編程依賴於函數和永恒性,所以函數調用總是返回相同的結果。通常,完美是在各方合理的平衡點上,所以大多數現代程式設計語言,如Kotlin或Scala,都在單一程式設計語言中,融合過程式編程和函數式編程方法,並在這兩方面擁有最為先進的理念。有些問題用函數式編程來解決更合適,而有些沿用過程式編程更直接。
在Android中用Kotlin語言實現Lamba運算式
Lambda運算式是定義匿名函數的簡單方法。由於Lambda運算式避免在抽象類別或介面中編寫明確的函式宣告,進而也避免了類的實現部分,所以它是非常有用的。在Kotlin語言中,可以將一函數作為另一函數的參數。例如,可以將需要回調(callback)的函數簡單化為:
1 fun runAsync(callback: () -> Unit) {2 ...3 callback()4 }
這個用法相當明確。完成一些轉換後(後續會看到的),函數的調用方式可以得到簡單化:
1 runAsync { toast("Finished") }
Kotlin語言的另一個出色方面是,允許用lambda運算式編寫介面,這樣可以巨大地簡化代碼。舉例說明更易明白,假設要編寫視圖的典型setOnClickListener()方法。
在Java語言中,介面代碼類似這樣:
1 public interface OnClickListener {2 void onClick(View v);3 }
然後,需要編寫匿名類實現這個介面:
1 view.setOnClickListener(new OnClickListener() {2 @Override3 public void onClick(View v) {4 Toast.makeText(v.getContext(), "Click", Toast.LENGTH_SHORT).show();5 }6 });
這段代碼轉換到Kotlin語言(用Anko toast函數)會是這樣:
1 view.setOnClickListener(object : OnClickListener {2 override fun onClick(v: View) {3 toast("Click")4 }5 })
如前所述,Kotlin語言允許對Java語言類庫進行一些最佳化,任何有介面的函數都可以由函數替代。就像定義setOnclickListener()方法那樣去運行:
1 fun setOnClickListener(listener: (View) -> Unit)
Lambda運算式由箭頭左側函數的參數(在圓括弧裡的內容)定義的,將值返回到箭頭右側。在這個例子中,得到的View返回給Unit(無參數)。按此思路,可以上述代碼略做簡化:
1 view.setOnClickListener({ view -> toast("Click")})
美妙的差異!在定義函數時,必須在箭頭的左側用方括弧,並指定參數值,而函數的執行代碼在箭頭右側。如果左側不使用參數,甚至可以省去左側部分:
1 view.setOnClickListener({ toast("Click") })
如果函數的最後一個參數是一個函數的話,可以將作為參數的函數移到圓括弧外面:
1 view.setOnClickListener() { toast("Click") }
最終,如果函數是唯一的參數,還可以去掉圓括弧:
1 view.setOnClickListener { toast("Click") }
與初期的Java語言代碼相比,代碼量小於原來的五分之一,且更容易理解。這實在是令人印象深刻。Anko給一個(本質上說是函數名的)簡化版本,由前面展示過的實現方法的擴充函數組成:
1 view.onClick { toast("Click") }
擴充程式設計語言
多虧有這些轉換,可以建立自己的產生器(builder)和代碼塊。Kotlin語言標準庫提供一些像with那樣有趣的函數。下面是更簡單的實現方式:
1 inline fun <T> with(t: T, body: T.() -> Unit) { t.body() }
這個函數會擷取一個類型T的對象和作為擴充函數使用的函數。實現的過程僅僅解決了對象,並讓對象執行函數。由於函數的第二個參數是另一個函數,所以可以將其移到括弧外面。這樣可以直接使用對象的this關鍵字建立代碼塊,還能夠直接使用對象的公用屬性和函數:
1 with(forecast) {2 Picasso.with(itemView.ctx).load(iconUrl).into(iconView)3 dateView.text = date4 descriptionView.text = description5 maxTemperatureView.text = "${high.toString()}º"6 minTemperatureView.text = "${low.toString()}º"7 itemView.onClick { itemClick(forecast) }8 }
總結
Lambda運算式的能量在於我們的想象力。如果沒有用過函數式編程方法,就需要大量的實踐,這樣做是值得的。如果你要進一步學習lambda運算式和Kotlin相關內容,可從我編寫的書中獲得。
釋放Android的函數式能量(I):Kotlin語言的Lambda運算式