博客主頁java
// lambda表達式顯現監聽器 button.setOnClickListener { /* ... */ }
data class Person( val name: String, val age: Int ) >>> val list = listOf(Person("kerwin", 12), Person("Bob", 23)) // 用lambda在集合中搜索,比較年齡找到最大的元素 >>> // fun <T, R : Comparable<R>> Iterable<T>.maxBy(selector: (T) -> R): T? // 接收一個集合中的元素做爲實參(使用it引用它)並返回用來比較的值,簡明寫法 // 若是隻有一個參數的lambda,且這個參數的類型能夠推導出來,會生成默認參數名稱it >>> println(list.maxBy { it.age }) // 花括號中的代碼片斷是lambda表達式,把它做爲實參傳給這個函數 // 這個lambda接收一個類型爲Person的參數並返回它的年齡 >>> list.maxBy { person -> person.age } // 若是lambda恰好是函數或者屬性的委託,能夠用成員引用替換 >>> println(list.maxBy(Person::age))
一個lambda把一段行爲進行編碼,能把它看成值處處傳遞,能夠被獨立的聲明並存儲到一個變量中。編程
// lambda表達式的語法 // 參數 -> 函數體 { x: Int, y: Int -> x + y } // kotlin 的lambda表達式始終用花括號({})包圍。 // 實參並無用括號括起來,箭頭把實參列表和lambda的函數體隔開
能夠把lambda表達式存儲在一個變量中,把這個變量看成普通函數對待segmentfault
// 使用變量存儲lambda,不能推導出參數類型,必須顯式的指定參數類型 val sum = { x: Int, y: Int -> x + y } println(sum(1, 2)) // 調用保存在變量中的lambda
kotlin中若是lambda表達式是函數調用的最後一個實參,它能夠放到括號的外邊app
list.maxBy() { person: Person -> person.age } // 當lambda是函數惟一的實參時,能夠去掉調用代碼中的空括號 // 顯式的寫出參數類型 list.maxBy { person: Person -> person.age } // 能夠不寫參數類型,會根據上下文推導出參數類型 list.maxBy { person -> person.age }
使用命名實參來傳遞lambdaide
val list = listOf(Person("kerwin", 12), Person("Bob", 23)) // 把lambda做爲命名實參傳遞 val names = list.joinToString(separator = " ", transform = { person: Person -> person.name }) println(names) // kerwin Bob // 能夠把lambda放在括號外傳遞 val names = list.joinToString(separator = " ") { person: Person -> person.name }
在函數內部使用lambda,也能夠訪問這個函數的參數,還能夠在lambda以前定義局部變量函數
// 在lambda中使用函數參數 fun printMessageWithPrefix(messages: Collection<String>, prefix: String) { messages.forEach { // lambda中訪問prefix參數 println("$prefix $it") } } val messages = listOf("404", "403") printMessageWithPrefix(messages, "error: ")
kotlin容許lambda內部訪問非final變量甚至修改它們,從lambda內訪問外部變量,稱這些變量被lambda捕捉。post
// 成員引用語法,使用::運算符 // 類::成員 Person::age
:: 運算符 能夠把函數轉換成一個值,如:val getAge = Person::age ,這種表達式稱爲成員引用ui
還能夠引用頂層函數(不是類的成員)this
fun test() = println("test") // 引用頂層函數,省略了類的名稱,直接 ::開頭 // 成員引用 ::test被看成實參傳遞給庫函數,它會調用相應的函數 run(::test)
若是lambda要委託給一個接收多個參數的函數,成員引用代替會很是方便編碼
// 這個lambda委託給sendEmail函數 val action = { person: Person, message: String -> sendEmail(person, message) } // 可使用成員引用代替 val nextAction= ::sendEmail >>> action(Person("kerwin",12), "吃飯") >>> nextAction(Person("bob",34), "吃飯")
能夠用 構造方法引用 存儲或者延期執行建立類實例的動做。構造方法引用的形式是在雙冒號(::)後指定類名稱
data class Person( val name: String, val age: Int ) // 構造方法引用,建立Person實例的動做被保存成了值 val createPerson = ::Person val person = createPerson("kerwin", 23) println(person)
還能夠引用擴展函數
// Person類擴展函數 fun Person.isAdult() = this.age >= 21 val person = Person("kerwin", 22) // 雖然isAdult不是Person類的成員,但還能夠經過引用訪問它 val predicate = Person::isAdult println(predicate(person))
filter 函數底層源碼:
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> { return filterTo(ArrayList<T>(), predicate) } public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) return destination }
從filter 函數源碼可知:filter函數遍歷集合並選出知足給定lambda後返回true的元素,這些知足條件的元素存放在新的集合中。
val list = listOf(1, 2, 3, 4) // 過濾出偶數元素 println(list.filter { it % 2 == 0 }) // [2, 4] val list = listOf(Person("kerwin", 23), Person("Bob", 12)) // 過濾出年齡超過18的人 println(list.filter { it.age >= 18 }) // [Person(name=kerwin, age=23)]
map 函數底層源碼:
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> { return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform) } public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C { for (item in this) destination.add(transform(item)) return destination }
從map 函數源碼可知:map函數對集合中的每個元素應用給定的lambda後並把結果存儲在一個新的集合中。新集合中元素個數不變,但每一個元素根據給定的lambda作了變換。
val list = listOf(1, 2, 3, 4) println(list.map { it * it }) // [1, 4, 9, 16] val list = listOf(Person("kerwin", 23), Person("Bob", 12)) // 只須要姓名列表,可使用map轉換 println(list.map { it.name }) // kerwin, Bob] // 可使用成員引用 list.map(Person::name) // filter 和 map 能夠組合使用 // 輸出年齡超過18歲的姓名 println(list.filter { it.age >= 18 }.map { it.name }) // [kerwin] // 找出年齡最大的人思路:先在集合中找到最大年齡,而後過濾最大年齡的人 val maxAge = list.maxBy(Person::age)?.age println(list.filter { it.age == maxAge })
還能夠對Map集合應用過濾和變換函數:
val map = mapOf(1 to "one", 2 to "two") println(map.mapValues { it.value.toUpperCase() }) // {1=ONE, 2=TWO} // 鍵和值分別由各自的函數來處理。 // filterKeys 和 mapKeys 過濾和變換 Map的鍵 // filterValues 和 mapValues 過濾和變換 Map的值
all 和 any 函數檢查集合中的全部元素是否都符合某個條件(或者它的變種,是否存在符合的元素);
count 函數檢查有多少元素知足判斷式;
find 函數返回第一個符合條件的元素。
all函數底層源碼:
/** * Returns `true` if all elements match the given [predicate]. */ public inline fun <T> Iterable<T>.all(predicate: (T) -> Boolean): Boolean { if (this is Collection && isEmpty()) return true for (element in this) if (!predicate(element)) return false return true }
從 all 函數源碼可知:集合中的全部元素都知足條件才返回true,不然返回false。
val list = listOf(Person("kerwin", 12), Person("bob", 19)) val result = list.all { person: Person -> person.age >= 18 } println(result) false
any 函數底層源碼:
public inline fun <T> Iterable<T>.any(predicate: (T) -> Boolean): Boolean { if (this is Collection && isEmpty()) return false for (element in this) if (predicate(element)) return true return false }
從 any 函數可知:集合中只要有一個元素知足條件就返回true,不然返回false
val list = listOf(Person("kerwin", 12), Person("bob", 19)) val result = list.any { person: Person -> person.age >= 18 } println(result) true
count 函數底層源碼:
public inline fun <T> Iterable<T>.count(predicate: (T) -> Boolean): Int { if (this is Collection && isEmpty()) return 0 var count = 0 for (element in this) if (predicate(element)) checkCountOverflow(++count) return count }
從 count 函數可知:集合中元素知足條件的個數
val result = list.count { person: Person -> person.age >= 10 } println(result) 2
find 函數底層源碼:
public inline fun <T> Iterable<T>.find(predicate: (T) -> Boolean): T? { return firstOrNull(predicate) } public inline fun <T> Iterable<T>.firstOrNull(predicate: (T) -> Boolean): T? { for (element in this) if (predicate(element)) return element return null }
從 find 函數可知:查找集合中第一個知足條件的元素,找到返回該元素,不然返回null
val result = list.find { person: Person -> person.age >= 18 } println(result) Person(name=bob, age=19)
groupBy 函數底層源碼:
public inline fun <T, K> Iterable<T>.groupBy(keySelector: (T) -> K): Map<K, List<T>> { return groupByTo(LinkedHashMap<K, MutableList<T>>(), keySelector) } public inline fun <T, K, M : MutableMap<in K, MutableList<T>>> Iterable<T>.groupByTo(destination: M, keySelector: (T) -> K): M { for (element in this) { val key = keySelector(element) val list = destination.getOrPut(key) { ArrayList<T>() } list.add(element) } return destination } public inline fun <K, V> MutableMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V { val value = get(key) return if (value == null) { val answer = defaultValue() put(key, answer) answer } else { value } }
從 groupBy 函數可知:把集合中全部的元素按照不一樣的特性分紅不一樣的分組,返回Map集合,key:分組條件。value:List集合,每一個分組都存儲在一個列表中。
val list = listOf( Person("kerwin", 12), Person("bob", 19), Person("Alice", 12) ) // 按照年齡分組,把相同年齡的人放在一組 val groupList = list.groupBy { person: Person -> person.age } println(groupList) {12=[Person(name=kerwin, age=12), Person(name=Alice, age=12)], 19=[Person(name=bob, age=19)]}
flatMap 函數底層源碼:
public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> { return flatMapTo(ArrayList<R>(), transform) } public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C { for (element in this) { val list = transform(element) destination.addAll(list) } return destination }
從 flatMap 函數可知:根據lambda給定的表達式(要返回的是Iterable子類)對每個元素作變換(或者說映射),而後把多個列表合併(或者說平鋪)成一個列表。
val books = listOf( Book("java", listOf("abc", "bcd")), Book("kotlin", listOf("wer")) ) // 統計集合中每本書的做者合併一個扁平的列表 val bookAllAuthors = books.flatMap { book: Book -> book.authors } println(bookAllAuthors) [abc, bcd, wer]
不少鏈式集合函數調用的時候,如:map 和 filter,這些函會及早的建立中間集合,也就是說每一步的中間結果都被存儲在一個臨時列表。序列 能夠避免建立這些臨時中間對象。
// 這種方式會建立臨時中間對象 // 特色:先在每一個元素上調用map函數,而後在結果列表中的每一個元素上再調用filter函數 list.map { println("map: $it") it.name } .filter { println("filter: $it") it.startsWith("k") } .toList() // 爲了提升效率,能夠把操做先變成序列,而不是直接使用集合 // 特色:全部操做是按照順序在每個元素上,處理完第一個元素(先映射再過濾),而後完成第二個元素的處理,以此類推 list.asSequence() // 把初始集合轉換成序列 .map { println("map: $it") it.name } // 序列支持和集合同樣的API .filter { println("filter: $it") it.startsWith("k") } .toList() // 把結果序列轉換回列表,反向轉換
kotlin惰性集合操做的入口就是:Sequence 接口,這個接口表示的就是一個能夠列舉元素的元素序列。它只提供了一個 方法iterator,用來從序列中獲取值。序列中的元素求值是惰性的,因此使用序列能夠更高效的對集合元素執行鏈式操做。
public interface Sequence<out T> { /** * Returns an [Iterator] that returns the values from the sequence. * * Throws an exception if the sequence is constrained to be iterated once and `iterator` is invoked the second time. */ public operator fun iterator(): Iterator<T> }
序列操做分爲兩類:中間的和末端的。一次 中間操做 返回的是另外一個序列,一個新序列知道如何變換原始序列中的元素;一次 末端操做 返回的是一個結果,這個結果多是集合、元素、數字或者從初始集合的變換序列中獲取的任意對象。
list.asSequence() .map(Person::name).filter { it.startsWith("k") } // 中間操做,始終是惰性的 .toList() // 末端操做
除了在集合上調用asSequence() 建立序列,還可使用generateSequence函數。
// 計算100之內全部天然數之和 val naturalNumbers = generateSequence(0) { it + 1 } val numbersTo100 = naturalNumbers.takeWhile { it <= 100 } // 當獲取結果sum時,全部被推遲的操做都被執行 println(numbersTo100.sum()) // 5050
takeWhile 函數在集合中底層源碼:
public inline fun <T> Iterable<T>.takeWhile(predicate: (T) -> Boolean): List<T> { val list = ArrayList<T>() for (item in this) { if (!predicate(item)) break list.add(item) } return list }
kotlin的lambda能夠無縫地和java API互操做。
能夠把lambda傳給任何指望函數式接口的方法。
//java void postponeComputation(int delay, Runnable computation) //kotlin中,能夠把lambda做爲實參傳給它,編譯器會自動轉換成一個Runnable 實例 // "一個Runnable 實例":指的是一個實現了Runnable 接口的匿名類的實例 // 整個程序只會建立一個Runnable實例 postponeComputation(1000) { println("kotlin") } // 也能夠把對象表達式做爲函數式接口的實現傳遞 // 這種方式每次調用都會建立一個新的實例 postponeComputation(1000,object : Runnable { override fun run() { } }) // 編程成全局的變量,程序中僅此一個實例,每次調用時都是同一個對象 val runnable = Runnable { println("kotlin") } fun handleComputation() { postponeComputation(1000, runnable) }
lambda從包圍它的做用域中捕捉了變量,每次調用就再也不可能重用同一個實例了
fun handleComputation(id: String) { // lambda會捕捉id這個變量 // 每次handleComputation調用時都會建立一個Runnable新實例 postponeComputation(1000) { println(id) } }
SAM構造方法 是編譯器生成的函數,讓你執行從lambda到函數式接口實例的顯式轉換。
帶單抽象方法的接口,叫做SAM接口
若是有一個方法返回的是一個函數式接口的實例,不能直接返回一個lambda,要用SAM構造方法把它包裝起來
// 使用SAM構造方法返回值 // SAM構造方法的名稱和底層函數式接口名稱同樣 // SAM構造方法只接收一個參數(一個被用做函數式接口單抽象方法體的lambda),並返回實現了這個接口的類的一個實例 fun callAllDoneRunnable() : Runnable { return Runnable { println("All Done.") } } callAllDoneRunnable().run()
SAM構造方法還能夠用在須要把從lambda生成的函數式接口實例存儲在一個變量中
// 使用SAM構造方法來重用listener實例 val listener = OnClickListener { view -> // 使用view.id來判斷點擊的是哪個按鈕 val text = when(view.id) { R.id.button1 -> "First Button" R.id.button1 -> "Second Button" else -> "Unknown Button" } toast(text) } button1.setOnClickListener(listener) button2.setOnClickListener(listener)
在lambda函數體內能夠調用一個不一樣對象的方法,並且無須藉助任何額外限定符,這樣的lambda叫做帶接收者的lambda
with 函數:能夠用它對同一個對象執行屢次操做,而不須要反覆把對象的名稱寫出來。
// 這段代碼調用了result 實例上好幾個不一樣的方法,且每次調用都重複result這個名稱 fun alphabet(): String { val result = StringBuilder() for (letter in 'A'..'Z') { result.append(letter) } result.append("\nNow I known the alphabet") return result.toString() }
使用 with 函數 重寫上段代碼,with 函數接收兩個參數。
// fun <T, R> with(receiver: T, block: T.() -> R): R // 使用with構造 fun alphabet(): String { val stringBuilder = StringBuilder() // 指定接受者的值 return with(stringBuilder) { for (letter in 'A'..'Z') { // 經過顯式的this來調用接收者值的方法 this.append(letter) } // 省略this能夠調用方法 append("\nNow I known the alphabet") // 從lambda返回值 this.toString() } }
使用with和一個表達式函數體來構建字母表
// 使用表達式函數體語法 fun alphabet() = with(StringBuilder()) { for (letter in 'A'..'Z') { append(letter) } append("\nNow I known the alphabet") toString() }
apply 函數始終會返回做爲實參傳遞給它的對象(換句話說:接收者對象)
// 它的接收者變成了做爲實參的lambda接收者,執行apply結果是StringBuilder fun alphabet() = StringBuilder().apply { for (letter in 'A'..'Z') { append(letter) } append("\nNow I known the alphabet") }.toString()
在java中,一般是經過另一個單獨的Builder對象來完成的;在kotlin中,能夠在任意對象上使用apply函數,徹底不須要自定義對象來完成。
// 使用apply初始化一個TextView fun createViewWithCustomAttributes(context: Context) = TextView(context).apply { text = "Sample Text" textSize = 20.0 setPading(10, 0, 0, 0) }
可使用kotlin標準庫函數buildString,它會負責建立StringBuilder並調用toString
fun alphabet() = buildString { for (letter in 'A'..'Z') { append(letter) } append("\nNow I known the alphabet") }
若是個人文章對您有幫助,不妨點個贊鼓勵一下(^_^)