Lambda作為形參和返回值
聲明高階函數
- 任何以lambda或者函數引用作為參數的函數,或者返回值,或者兩者都有,就是高階函數。比如list.filter
(4,"abc")-> {}
如下:
{ x, y -> x + y}
這里省略了參數x,y類型,因為在其他部分已經制定了,不需要在lambda函數體再次聲明
val funOrNull: ((Int, Int) -> Int)? = null
復制代碼
- 可以為函數參數制定名字
//聲明
fun performRequest(url: String,callback: (code: Int, content: String) -> Unit
){}
//調用
val url = "http://baidu.com"
performRequest(url,{code, content -> /*code*/ })
performRequest(url){code, content -> /*code*/ }//根據kotlin約定,lambda為最后一個參數,可以放到括號外面
復制代碼
- 調用函數
//聲明一個函數,參數為一個函數,這個函數有2個參數都是int值,返回值也為int
fun towAndThree(operator: (x: Int, y: Int) -> Int) {//這里的x,y是可以省略的,省略了,IDE代碼補全會不方便val result = operator(3, 4)print("the result is $result")
}
復制代碼
- filter()函數
//聲明
public inline fun <C : Appendable> CharSequence.filterTo(destination: C, predicate: (Char) -> Boolean): C {for (index in 0 until length) {val element = get(index)if (predicate(element)) destination.append(element)}
return destination
}
//調用
println(url.filter { it in 'a'..'z' })
復制代碼
- 在Java中使用函數類 原理:函數類型被聲明為普通的接口:一個函數類型的變量,是
FunctionN
接口的一個實現。Kotlin
標準庫定義了一系列的接口比如Function0<R>
(沒有參數的函數),Function1(P1,R)
(一個參數的函數),調用這個方法就會執行函數。一個函數類型的變量就實現了對應的FunctionN
接口的實現類的實例,實現類invoke
方法包含了lambda
函數體
//kotlin聲明
fun processTheAnser(f: (Int) -> Int){}
//java 調用 java 8
processTheAnder(num->num+1)
//在Java8以下的版本這樣調用
procressTheAnder{new Function1<Integer,Integer>(){@Overridepublic Integer invoke(Integer number){return number + 1}}
}
復制代碼
函數類型的參數默認值和null
值
fun <T> Collection<T>.joinToString(separator: String = ", ",prefix: String = "",postfix: String = ""
): String {val result = StringBuilder(prefix)for ((index, value) in this.withIndex()) {if (index > 0) result.append(separator)result.append(value.toString())}result.append(postfix)return result.toString()
}
復制代碼
返回函數的函數
- 比如運輸費用依賴于運輸方式,可以定義一個函數,用來選擇恰當的邏輯變體,并將它作為一個函數返回
fun getShoppingCarCacul(delivery: Delivery): (Order) -> Double {if (delivery == Delivery.STANDARD) {return { order: Order -> 6 + 2.1 * order.itemCont }}return { order -> 1.2 * order.itemCont }
}
//調用
var shoppingCarCacul = getShoppingCarCacul(Delivery.EXPEDITED)
println("產生的運費:${shoppingCarCacul(Order(4))}")
復制代碼
- eg:通過輸入開頭文字,過濾聯系人列表
data class Man(val firstName: String,val lastName: String,val phoneNumber: String?
)class ContactListFilter() {val prefix = ""val isOnlyPhoneNumber = falsefun qetPredicate(): (Man) -> Boolean {val startWithPrefix = { man: Man -> man.firstName.startsWith(prefix) || man.lastName.startsWith(prefix) }//這里不能使用it,因為IDE推斷不出it代表是什么if (!isOnlyPhoneNumber) {return startWithPrefix}return { man: Man -> startWithPrefix(man) && man.phoneNumber != null }//這里可以是用it,這里是根據方法的返回值類型,推斷出it代碼得man}
}
//調用val mans = listOf(Man("Bob", "jack"), Man("BooBo", "jack", "13800138000"), Man("kk", "jack", "13800138000"))val contactListFilter = ContactListFilter()with(contactListFilter) {prefix = "B"isOnlyPhoneNumber = false}
println(mans.filter(contactListFilter.qetPredicate()))
復制代碼
使用lambda去除重復代碼
val log = listOf(SiteVisit("/", 34.0, OS.WINDOWS),SiteVisit("/", 22.0, OS.MAC),SiteVisit("/", 12.0, OS.WINDOWS),SiteVisit("/sign_up", 8.0, OS.IOS),SiteVisit("/", 16.3, OS.ANDROID))
fun List<SiteVisit>.averageTime(os: OS) = filter { it.os == os }.map { it.duration }.average()//調用
println(log.averageTime(OS.WINDOWS))
復制代碼
- 如果說要查詢WINDOW、和Android的呢?后面又需要改成來自iOS的注冊頁面停留時間是多少呢?這個時候lambda就派上用場了。
fun List<SiteVisit>.averageTime(predicate: (SiteVisit) -> Boolean) = filter(predicate)//這里的篩選改為lambda。.map { it.duration }.average()
復制代碼
把過濾條件轉換成lambda表達式,實現去除重復代碼
策略模式(你需要聲明一個接口,并且為每一種可能實現不同的策略)可以通過lambda簡化
內聯函數:消除lambda運行時帶來的開銷
在kotlin中,每創建一個lambda
表達式就會創建一個匿名類,so,每次調用都會創建一個對象,會帶來額外的開銷這個時候inline
就出現了。
一個被inline修飾的函數,函數體會直接替換到函數被調用的地方,而不是正常調用。一般來說,參數如果被直接調用或者作為參數傳遞給另外一個inline
函數。他是可以被內聯的。
- 內聯函數使用限制-》不能把內聯函數保存到一個屬性?
內聯集合操作
Kotlin
標準庫中,集合函數,比如filter
,map
等函數已經inline函數,不會產生額外的對象。在處理比較大的集合,應該使用序列asSequence
進行操作。
怎樣決定是否使用lambda
- 使用inline關鍵字只能提高帶有lambda參數的函數的性能。
- 對于普通函數JVM已經提供了強大的內聯支持。
- 應該保證inline修飾的函數比較小。-》因為需要把函數字節碼拷貝到每一個調用點上。
使用lambda管理資源
通常在資源管理中,需要在try
里獲取資源,在finally
中釋放資源可以把這一部分封裝成lambda
表達式 在Kotlin中,加鎖使用withLock
函數 :try-with-resource 語句
static String readFirstLineFromFile(String path) throws IOException {try (BufferedReader br = new BufferedReader(new FileReader(path))) {return br.readLine();}//這里不需要寫關閉資源的語句,
}
復制代碼
在Kotlin中使用use
實現相同的結果,用于操作可關閉的資源。
fun readFirstLineFromFile(path: String): String = BufferedReader(FileReader(path)).use { br -> br.readLine() }
復制代碼
高階函數中的控制流
- 使用return 從一個封閉的函數中返回。
val list = listOf(Person("Bob", 18, 20000), Person("Jack", 19, 20000))list.forEach {if (it.name=="Bob") {println("Found!")return //直接返回方法}}
復制代碼
- 從lambda返回
val list = listOf(Person("Bob", 18, 20000), Person("Jack", 19, 20000))list.forEach {if (it.name=="Bob") {println("Found!")return@forEach //直接返回方法}}
復制代碼
同樣的規則也適用于this表達式
- 匿名函數:默認使用局局部返回
list.forEach {if (it.name=="Bob") returnprintln("Found!")}
復制代碼
在filter
中使用匿名函數
list.filter(fun(person): Boolean {return person.age > 10})
復制代碼
如果是使用表達式函數體,可以省略返回值類型,可以簡化為
list.filter(fun(person) = person.age > 10
)
復制代碼
End