通過前面一系列對Kotlin
講解,相信你們已經能對Kotlin
有了一個基本的認識。若是你又Java
語言方面的編程經驗,你可能已經不知足前面的基礎語法了。從這篇文章起,就爲你們講解Kotlin
語言中的高級操做。
Lambda
語法在Java
中已經被普遍的運用,咱們在開發Android
中幾乎上每個項目也會在項目中接入Lambda
插件,由於Lambda
確實能簡少不少的代碼量。無獨有偶,在Kotlin
中也是Lambda
語法的,在這篇文章中就詳細的爲你們講解Lambda
語法的編寫與使用,同時會後面的Kotlin——高級篇(二):高階函數詳解與標準的高階函數使用打下基礎。html
在上面已經提到了在
Java
中已經被普遍的運用,可是也是在Java8
的時候才支持這種Lambda
表達式。在其餘的編程語言中(例如:Scala
語言)。而這種表達式是語法糖中的一種。值得慶幸的是,Kotlin
一經開源成熟就已經支持這種語法。git
Lambda
表達式的本質實際上是匿名函數
,由於在其底層實現中仍是經過匿名函數
來實現的。可是咱們在用的時候沒必要關心起底層實現。不過Lambda
的出現確實是減小了代碼量的編寫,同時也是代碼變得更加簡潔明瞭。
Lambda
做爲函數式編程的基礎,其語法也是至關簡單的。這裏先經過一段簡單的代碼演示沒讓你們瞭解Lambda
表達式的簡潔之處。github
例:編程
// 這裏舉例一個Android中最多見的按鈕點擊事件的例子
mBtn.setOnClickListener(object : View.OnClickListener{
override fun onClick(v: View?) {
Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show()
}
})
複製代碼
等價於數組
// 調用
mBtn.setOnClickListener { Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show() }
複製代碼
關於Lambda
的使用,我這裏從兩個方面講解,一是先介紹Lambda
表達式的特色,二是從Lambda
的語法使用講解。閉包
古人云:欲取之,先與之。編程語言
要學習Lambda
表達式語法,必先了解其特色。我在這裏先總結出Lambda
表達式的一些特徵。在下面講解到Lambda
語法與實踐時你們就明白了。即:ide
Lambda
表達式老是被大括號括着- 其參數(若是存在)在
->
以前聲明(參數類型能夠省略)- 函數體(若是存在)在
->
後面。
爲了讓你們完全的弄明白Lambda
語法,我這裏用三種用法來說解。而且舉例爲你們說明函數式編程
語法以下:函數
1. 無參數的狀況 :
val/var 變量名 = { 操做的代碼 }
2. 有參數的狀況
val/var 變量名 : (參數的類型,參數類型,...) -> 返回值類型 = {參數1,參數2,... -> 操做參數的代碼 }
可等價於
// 此種寫法:即表達式的返回值類型會根據操做的代碼自推導出來。
val/var 變量名 = { 參數1 : 類型,參數2 : 類型, ... -> 操做參數的代碼 }
3. lambda表達式做爲函數中的參數的時候,這裏舉一個例子:
fun test(a : Int, 參數名 : (參數1 : 類型,參數2 : 類型, ... ) -> 表達式返回類型){
...
}
複製代碼
實例講解:
無參數的狀況
// 源代碼
fun test(){ println("無參數") }
// lambda代碼
val test = { println("無參數") }
// 調用
test() => 結果爲:無參數
複製代碼
有參數的狀況,這裏舉例一個兩個參數的例子,目的只爲你們演示
// 源代碼
fun test(a : Int , b : Int) : Int{
return a + b
}
// lambda
val test : (Int , Int) -> Int = {a , b -> a + b}
// 或者
val test = {a : Int , b : Int -> a + b}
// 調用
test(3,5) => 結果爲:8
複製代碼
lambda表達式做爲函數中的參數的時候
// 源代碼
fun test(a : Int , b : Int) : Int{
return a + b
}
fun sum(num1 : Int , num2 : Int) : Int{
return num1 + num2
}
// 調用
test(10,sum(3,5)) // 結果爲:18
// lambda
fun test(a : Int , b : (num1 : Int , num2 : Int) -> Int) : Int{
return a + b.invoke(3,5)
}
// 調用
test(10,{ num1: Int, num2: Int -> num1 + num2 }) // 結果爲:18
複製代碼
最後一個的實現可能你們難以理解,但請不要迷茫,你繼續看下去,在下面的實踐和高階函數中會爲你們介紹。
通過上面的實例講解與語法的介紹,咱們對其做出一個總結:
lambda
表達式老是被大括號括着。- 定義完整的
Lambda
表達式如上面實例中的語法2,它有其完整的參數類型標註,與表達式返回值。當咱們把一些類型標註省略的狀況下,就如上面實例中的語法2的另一種類型。當它推斷出的返回值類型不爲'Unit'時,它的返回值即爲->
符號後面代碼的最後一個(或只有一個)表達式的類型。- 在上面例子中語法3的狀況表示爲:
高階函數
,當Lambda
表達式做爲其一個參數時,只爲其表達式提供了參數類型與返回類型,因此,咱們在調用此高階函數的時候咱們要爲該Lambda
表達式寫出它的具體實現。
invoke()
函數:表示爲經過函數變量
調用自身,由於上面例子中的變量b
是一個匿名函數。學會了上面講解的語法只是,相信您已能大體的編寫且使用lambda
表達式了,不過只會上面簡單的語法還不足以運用於實際項目中複雜的狀況。下面從幾個知識點講解Lambda
實踐的要點。
it
並非Kotlin
中的一個關鍵字(保留字)。it
是在當一個高階函數中Lambda
表達式的參數只有一個的時候可使用it
來使用此參數。it
可表示爲單個參數的隱式名稱,是Kotlin
語言約定的。
例1:
val it : Int = 0 // 即it不是`Kotlin`中的關鍵字。可用於變量名稱
複製代碼
例2:單個參數的隱式名稱
// 這裏舉例一個語言自帶的一個高階函數filter,此函數的做用是過濾掉不知足條件的值。
val arr = arrayOf(1,3,5,7,9)
// 過濾掉數組中元素小於2的元素,取其第一個打印。這裏的it就表示每個元素。
println(arr.filter { it < 5 }.component1())
複製代碼
例2這個列子只是給你們it
的使用,filter
高階函數,在後面的Kotlin——高級篇(四):集合(Array、List、Set、Map)基礎章節中會爲你們詳細講解,這裏很少作介紹。下面爲咱們本身寫一個高階函數去講解it
。關於高階函數的定義與使用請參見Kotlin——高級篇(二):高階函數詳解與標準的高階函數使用這篇文章。
例3:
fun test(num1 : Int, bool : (Int) -> Boolean) : Int{
return if (bool(num1)){ num1 } else 0
}
println(test(10,{it > 5}))
println(test(4,{it > 5}))
複製代碼
輸出結果爲:
10
0
複製代碼
代碼講解:上面的代碼意思是,在高階函數test
中,其返回值爲Int
類型,Lambda
表達式以num1
位條件。其中若是Lambda
表達式的值爲false
的時候返回0,反之返回num1
。故而當條件爲num1 > 5
這個條件時,當調用test
函數,num1 = 10
返回值就是10,num1 = 4
返回值就是0。
在使用
Lambda
表達式的時候,能夠用下劃線(_
)表示未使用的參數,表示不處理這個參數。
同時在遍歷一個Map
集合的時候,這當很是有用。
舉例:
val map = mapOf("key1" to "value1","key2" to "value2","key3" to "value3")
map.forEach{
key , value -> println("$key \t $value")
}
// 不須要key的時候
map.forEach{
_ , value -> println("$value")
}
複製代碼
輸出結果:
key1 value1
key2 value2
key3 value3
value1
value2
value3
複製代碼
- 匿名函數的特色是能夠明確指定其返回值類型。
- 它和常規函數的定義幾乎類似。他們的區別在於,匿名函數沒有函數名。
例:
fun test(x : Int , y : Int) : Int{ fun(x : Int , y : Int) : Int{
常規函數: return x + y 匿名函數: return x + y
} }
複製代碼
在前面的Kotlin——初級篇(七):函數基礎總結咱們講解過單表達式函數。故而,能夠簡寫成下面的方式。
常規函數 : fun test(x : Int , y : Int) : Int = x + y
匿名函數 : fun(x : Int , y : Int) : Int = x + y
複製代碼
從上面的兩個例子能夠看出,匿名函數與常規函數的區別在於一個有函數名,一個沒有。
實例演練:
val test1 = fun(x : Int , y : Int) = x + y // 當返回值能夠自動推斷出來的時候,能夠省略,和函數同樣
val test2 = fun(x : Int , y : Int) : Int = x + y
val test3 = fun(x : Int , y : Int) : Int{
return x + y
}
println(test1(3,5))
println(test2(4,6))
println(test3(5,7))
複製代碼
輸出結果爲:
8
10
12
複製代碼
從上面的代碼咱們能夠總結出匿名函數
與Lambda
表達式的幾點區別:
- 匿名函數的參數傳值,老是在小括號內部傳遞。而
Lambda
表達式傳值,能夠有省略小括號的簡寫寫法。- 在一個不帶
標籤
的return
語句中,匿名函數時返回值是返回自身函數的值,而Lambda
表達式的返回值是將包含它的函數中返回。
在
kotlin
中,提供了指定的接受者對象調用Lambda
表達式的功能。在函數字面值的函數體中,能夠調用該接收者對象上的方法而無需任何額外的限定符。它相似於擴展函數
,它允你在函數體內訪問接收者對象的成員。
匿名函數語法容許你直接指定函數字面值的接收者類型,若是你須要使用帶接收者的函數類型聲明一個變量,並在以後使用它,這將很是有用。
例:
val iop = fun Int.( other : Int) : Int = this + other
println(2.iop(3))
複製代碼
輸出結果爲:
5
複製代碼
要用Lambda表達式做爲接收者類型的前提是接收着類型能夠從上下文中推斷出來。
例:這裏用官方的一個例子作說明
class HTML {
fun body() { …… }
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // 建立接收者對象
html.init() // 將該接收者對象傳給該 lambda
return html
}
html { // 帶接收者的 lambda 由此開始
body() // 調用該接收者對象的一個方法
}
複製代碼
- 所謂
閉包
,便是函數中包含函數,這裏的函數咱們能夠包含(Lambda
表達式,匿名函數,局部函數,對象表達式)。咱們熟知,函數式編程是如今和將來良好的一種編程趨勢。故而Kotlin
也有這一個特性。- 咱們熟知,
Java
是不支持閉包的,Java
是一種面向對象的編程語言,在Java
中,對象
是他的一等公民。函數
和變量
是二等公民。Kotlin
中支持閉包,函數
和變量
是它的一等公民,而對象
則是它的二等公民了。
實例:看一段Java
代碼
public class TestJava{
private void test(){
private void test(){ // 錯誤,由於Java中不支持函數包含函數
}
}
private void test1(){} // 正確,Java中的函數只能包含在對象中+
}
複製代碼
實例:看一段Kotlin
代碼
fun test1(){
fun test2(){ // 正確,由於Kotlin中能夠函數嵌套函數
}
}
複製代碼
下面咱們講解Kotlin
中幾種閉包的表現形式。
例:讓函數返回一個函數,並攜帶狀態值
fun test(b : Int): () -> Int{
var a = 3
return fun() : Int{
a++
return a + b
}
}
val t = test(3)
println(t())
println(t())
println(t())
複製代碼
輸出結果:
7
8
9
複製代碼
例:
var sum : Int = 0
val arr = arrayOf(1,3,5,7,9)
arr.filter { it < 7 }.forEach { sum += it }
println(sum)
複製代碼
輸出結果:
9
複製代碼
Android
開發中爲RecyclerView
的適配器編寫一個Item
點擊事件class TestAdapter(val context : Context , val data: MutableList<String>)
: RecyclerView.Adapter<TestAdapter.TestViewHolder>(){
private var mListener : ((Int , String) -> Unit)? = null
override fun onBindViewHolder(holder: TestViewHolder?, position: Int) {
...
holder?.itemView?.setOnClickListener {
mListener?.invoke(position, data[position])
}
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): TestViewHolder {
return TestViewHolder(View.inflate(context,layoutId,parent))
}
override fun getItemCount(): Int {
return data.size
}
fun setOnItemClickListener(mListener : (position : Int, item : String) -> Unit){
this.mListener = mListener
}
inner class TestViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView)
}
// 調用
TestAdapter(this,dataList).setOnItemClickListener { position, item ->
Toast.makeText(this,"$position \t $item",Toast.LENGTH_SHORT).show()
}
複製代碼
Lambda
表達式是爲咱們減小了大量的代碼,可是Lambda
表達式是爲後面的高階函數章節打下基礎,雖然在這篇文章中也提到了高階函數,可是都是最基礎的,在下一節中會爲你們介紹自定義高階函數與Kotlin
自身中經常使用的高階函數講解。
在這一章節中,講述了Lambda
的語法、使用。以及Lambda
表達式的一些特性與實踐操做。固然還包含了匿名函數
這一知識點。其中最重要的當屬Lambda
的實踐操做。若是你看完這篇文章還不甚理解,請在仔細的閱讀一遍並實際代碼演練,由於在後面的高階函數章節還會遇到。
在這最後但願您能給個關注,由於您的關注,是我繼續寫文章最好的動力。
個人我的博客:Jetictors
Github:Jteictors