五框弛、Lambda編程
1.Lambda表達(dá)式和成員引用
Lambda簡(jiǎn)介:作為函數(shù)參數(shù)的代碼塊。可以理解為簡(jiǎn)化表達(dá)后的匿名函數(shù)冶共,實(shí)質(zhì)上它就是一種語法糖。
用匿名內(nèi)部類實(shí)現(xiàn)監(jiān)聽器:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("TAG", "zwm, test java")
}
});
用lambda實(shí)現(xiàn)監(jiān)聽器:
button.setOnClickListener{ Log.d(TAG, "zwm, test lambda") }
Lambda和集合
手動(dòng)在集合中搜索:
class Person(val name: String, val age: Int)
fun method() {
val people = listOf(Person("Android", 10), Person("Java", 20), Person("Kotlin", 5))
var maxAge = 0
var theOldest: Person? = null
for (person in people) {
if (person.age > maxAge) {
maxAge = person.age
theOldest = person
}
}
Log.d("TAG", "zwm, theOldest: ${theOldest?.name} ${theOldest?.age}")
}
日志打用拷纭:
2020-08-19 15:39:44.995 15807-15807/? D/TAG: zwm, theOldest: Java 20
用lambda在集合中搜索:
class Person(val name: String, val age: Int)
fun method() {
val people = listOf(Person("Android", 10), Person("Java", 20), Person("Kotlin", 5))
var theOldest = people.maxBy { it.age }
Log.d("TAG", "zwm, theOldest: ${theOldest?.name} ${theOldest?.age}")
}
日志打油苯:
2020-08-19 15:41:39.258 16117-16117/com.tomorrow.kotlindemo D/TAG: zwm, theOldest: Java 20
maxBy函數(shù)可以在任何集合上調(diào)用,且只需要一個(gè)實(shí)參:一個(gè)函數(shù)眨层,指定比較哪個(gè)值來找到最大元素庙楚。
如果lambda剛好是函數(shù)或者屬性的委托,可以用成員引用替換:
class Person(val name: String, val age: Int)
fun method() {
val people = listOf(Person("Android", 10), Person("Java", 20), Person("Kotlin", 5))
var theOldest = people.maxBy(Person::age )
Log.d("TAG", "zwm, theOldest: ${theOldest?.name} ${theOldest?.age}")
}
日志打优坑!:
2020-08-19 15:47:17.127 16493-16493/com.tomorrow.kotlindemo D/TAG: zwm, theOldest: Java 20
Lambda表達(dá)式的語法
{ x: Int, y: Int -> x + y } //花括號(hào)馒闷,參數(shù) -> 函數(shù)體
可以把lambda表達(dá)式存儲(chǔ)在一個(gè)變量中,把這個(gè)變量當(dāng)做普通函數(shù)對(duì)待(即通過相應(yīng)實(shí)參調(diào)用它):
fun method() {
val sum = { x: Int, y: Int -> x + y }
Log.d("TAG", "zwm, sum: ${sum(1, 2)}")
}
日志打尤鳌:
2020-08-19 15:55:09.737 18193-18193/? D/TAG: zwm, sum: 3
還可以直接調(diào)用lambda表達(dá)式:
fun method() {
Log.d("TAG", "zwm, sum: ${{ x: Int, y: Int -> x + y }(1, 2)}")
}
日志打幽烧恕:
2020-08-19 15:55:51.879 18556-18556/com.tomorrow.kotlindemo D/TAG: zwm, sum: 3
可以使用庫(kù)函數(shù)run來執(zhí)行傳遞給它的lambda:
fun method() {
Log.d("TAG", "zwm, sum: ${run{{ x: Int, y: Int -> x + y }(1, 2)}}")
}
日志打印:
2020-08-19 16:01:36.906 19393-19393/com.tomorrow.kotlindemo D/TAG: zwm, sum: 3
Lambda表達(dá)式的簡(jiǎn)明語法推導(dǎo)
最初版本:
class Person(val name: String, val age: Int)
fun method() {
val people = listOf(Person("Android", 10), Person("Java", 20), Person("Kotlin", 5))
var theOldest = people.maxBy({ p: Person -> p.age })
Log.d("TAG", "zwm, theOldest: ${theOldest?.name} ${theOldest?.age}")
}
Kotlin有這樣一種語法約定:如果lambda表達(dá)式是函數(shù)調(diào)用的最后一個(gè)實(shí)參捺疼,它可以放到括號(hào)的外邊:
fun method() {
val people = listOf(Person("Android", 10), Person("Java", 20), Person("Kotlin", 5))
var theOldest = people.maxBy(){ p: Person -> p.age }
Log.d("TAG", "zwm, theOldest: ${theOldest?.name} ${theOldest?.age}")
}
當(dāng)lambda是函數(shù)唯一的實(shí)參時(shí)疏虫,還可以去掉調(diào)用代碼中的空括號(hào)對(duì):
fun method() {
val people = listOf(Person("Android", 10), Person("Java", 20), Person("Kotlin", 5))
var theOldest = people.maxBy{ p: Person -> p.age }
Log.d("TAG", "zwm, theOldest: ${theOldest?.name} ${theOldest?.age}")
}
和局部變量一樣,如果lambda參數(shù)的類型可以被推導(dǎo)出來,就不需要顯式地指定它:
fun method() {
val people = listOf(Person("Android", 10), Person("Java", 20), Person("Kotlin", 5))
var theOldest = people.maxBy{ p -> p.age }
Log.d("TAG", "zwm, theOldest: ${theOldest?.name} ${theOldest?.age}")
}
如果當(dāng)前上下文期望的是只有一個(gè)參數(shù)的lambda且這個(gè)參數(shù)的類型可以推斷出來,可以使用默認(rèn)參數(shù)名稱it代替命名參數(shù):(僅在實(shí)參名稱沒有顯式地指定時(shí)這個(gè)默認(rèn)的名稱才會(huì)生成)
fun method() {
val people = listOf(Person("Android", 10), Person("Java", 20), Person("Kotlin", 5))
var theOldest = people.maxBy{ it.age }
Log.d("TAG", "zwm, theOldest: ${theOldest?.name} ${theOldest?.age}")
}
如果用變量存儲(chǔ)lambda葛超,那么就沒有可以推斷出參數(shù)類型的上下文,必須顯式地指定參數(shù)類型:
fun method() {
val people = listOf(Person("Android", 10), Person("Java", 20), Person("Kotlin", 5))
val getAge = { p: Person -> p.age }
var theOldest = people.maxBy(getAge)
Log.d("TAG", "zwm, theOldest: ${theOldest?.name} ${theOldest?.age}")
}
如果Lambda表達(dá)式返回的不是Unit翅敌,那么默認(rèn)最后一行表達(dá)式的值類型就是返回值類型。
在作用域中訪問變量
當(dāng)在函數(shù)內(nèi)聲明一個(gè)匿名內(nèi)部類的時(shí)候醇锚,能夠在這個(gè)匿名內(nèi)部類內(nèi)部引用這個(gè)函數(shù)的參數(shù)和局部變量哼御。也可以用lambda做同樣的事情坯临。如果在函數(shù)內(nèi)部使用lambda,也可以訪問這個(gè)函數(shù)的參數(shù)恋昼,還有在lambda之前定義的局部變量看靠。
在lambda中使用函數(shù)參數(shù):
fun method(name: String, age: Int) {
{ Log.d("TAG", "zwm, name: $name, age: $age") }()
}
日志打印:
2020-08-19 16:48:51.001 26858-26858/com.tomorrow.kotlindemo D/TAG: zwm, name: hello, age: 9
在lambda中改變局部變量:
fun method(name: String, age: Int) {
var address = "bj"
{
address = "gz"
Log.d("TAG", "zwm, name: $name, age: $age, address: $address")
}()
Log.d("TAG", "zwm, address: $address")
}
日志打右杭 :
2020-08-19 17:06:33.875 28625-28625/? D/TAG: zwm, name: hello, age: 9, address: gz
2020-08-19 17:06:33.876 28625-28625/? D/TAG: zwm, address: gz
和Java不一樣挟炬,Kotlin允許在lambda內(nèi)部訪問非final變量甚至修改它們。從lambda內(nèi)訪問外部變量嗦哆,稱這些變量被lambda捕捉谤祖。默認(rèn)情況下,局部變量的生命期被限制在聲明這個(gè)變量的函數(shù)中老速。但是如果它被lambda捕捉了粥喜,使用這個(gè)變量的代碼可以被存儲(chǔ)并稍后再執(zhí)行。
成員引用語法:
類::成員 //雙冒號(hào)把類名稱與要引用的成員(一個(gè)方法或者一個(gè)屬性)名稱隔開
引用屬性:
class Person(val name: String, val age: Int)
fun method() {
val people = listOf(Person("Android", 10), Person("Java", 20), Person("Kotlin", 5))
var theOldest = people.maxBy(Person::age )
Log.d("TAG", "zwm, theOldest: ${theOldest?.name} ${theOldest?.age}")
}
日志打娱偃:
2020-08-19 17:14:23.246 28854-28854/com.tomorrow.kotlindemo D/TAG: zwm, theOldest: Java 20
引用頂層函數(shù):
fun topMethod(language: String) {
Log.d("TAG", "zwm, this is top method: $language")
}
fun method() {
(::topMethod)("Kotlin")
}
2020-08-19 17:40:17.769 9798-9798/com.tomorrow.kotlindemo D/TAG: zwm, this is top method: Kotlin
如果lambda要委托給一個(gè)接收多個(gè)參數(shù)的函數(shù)额湘,提供成員引用代替它將會(huì)非常方便:
class Person(val name: String, val age: Int)
fun sendEmail(person: Person, content: String) {
Log.d("TAG", "zwm, sendEmail")
}
fun method() {
val action = { person: Person, content: String -> sendEmail(person, content) } //這個(gè)lambda委托給sendEmail函數(shù)
val nextAction = ::sendEmail //可以用成員引用代替
}
可以用構(gòu)造方法引用存儲(chǔ)或者延期執(zhí)行創(chuàng)建類實(shí)例的動(dòng)作。構(gòu)造方法引用的形式是在雙冒號(hào)后指定類名稱:
class Person(val name: String, val age: Int)
fun method() {
val createPerson = ::Person
val p = createPerson("Kotlin", 5)
}
還可以用同樣的方式引用擴(kuò)展函數(shù):
class Person(val name: String, val age: Int)
fun Person.isAdult() = age >= 21
fun method() {
val adult = Person::isAdult //盡管isAdult是擴(kuò)展函數(shù)不是Person類的成員旁舰,還是可以通過引用訪問它
val person = Person("Android", 10)
Log.d("TAG", "zwm, ${adult(person)}")
Log.d("TAG", "zwm, ${person.isAdult()}")
}
日志打臃婊:
2020-08-19 18:05:07.156 14079-14079/com.tomorrow.kotlindemo D/TAG: zwm, false
2020-08-19 18:05:07.156 14079-14079/com.tomorrow.kotlindemo D/TAG: zwm, false
綁定引用:
class Person(val name: String, val age: Int)
fun method() {
val p = Person("Android", 10)
val ageFunction = Person::age //使用類引用成員
Log.d("TAG", "zwm, ageFunction: ${ageFunction(p)}")
val ageFunction2 = p::age //使用對(duì)象引用成員
Log.d("TAG", "zwm, ageFunction2: ${ageFunction2()}")
}
日志打印:
2020-08-19 19:43:57.800 20766-20766/com.tomorrow.kotlindemo D/TAG: zwm, ageFunction: 10
2020-08-19 19:43:57.801 20766-20766/com.tomorrow.kotlindemo D/TAG: zwm, ageFunction2: 10
2.集合的函數(shù)式API
基礎(chǔ):filter和map
filter函數(shù)遍歷集合并選出應(yīng)用給定lambda后會(huì)返回true的那些元素箭窜,結(jié)果是一個(gè)新的集合:
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Kotlin", 5), Person("Java", 20), Person("Android", 10))
val result = list.filter { it.age >= 10 }
for((index, item) in result.withIndex())
Log.d("TAG", "zwm, index: $index, name: ${item.name}")
}
日志打犹夯馈:
2020-08-24 00:51:35.432 20309-20309/com.tomorrow.kotlindemo D/TAG: zwm, index: 0, name: Java
2020-08-24 00:51:35.432 20309-20309/com.tomorrow.kotlindemo D/TAG: zwm, index: 1, name: Android
map函數(shù)對(duì)集合中的每一個(gè)元素應(yīng)用給定的函數(shù)并把結(jié)果收集到一個(gè)新集合:
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Kotlin", 5), Person("Java", 20), Person("Android", 10))
val result = list.map { it.name }
for((index, item) in result.withIndex())
Log.d("TAG", "zwm, index: $index, item: $item")
}
日志打印:
2020-08-24 01:03:01.554 20756-20756/com.tomorrow.kotlindemo D/TAG: zwm, index: 0, item: Kotlin
2020-08-24 01:03:01.554 20756-20756/com.tomorrow.kotlindemo D/TAG: zwm, index: 1, item: Java
2020-08-24 01:03:01.554 20756-20756/com.tomorrow.kotlindemo D/TAG: zwm, index: 2, item: Android
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Kotlin", 5), Person("Java", 20), Person("Android", 10))
val result = list.map(Person::name)
for((index, item) in result.withIndex())
Log.d("TAG", "zwm, index: $index, item: $item")
}
日志打踊怯!:
2020-08-24 01:04:08.179 21530-21530/? D/TAG: zwm, index: 0, item: Kotlin
2020-08-24 01:04:08.179 21530-21530/? D/TAG: zwm, index: 1, item: Java
2020-08-24 01:04:08.179 21530-21530/? D/TAG: zwm, index: 2, item: Android
filter和map:
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Kotlin", 5), Person("Java", 20), Person("Android", 10))
val result = list.filter { it.age >= 10 }.map(Person::name)
for((index, item) in result.withIndex())
Log.d("TAG", "zwm, index: $index, item: $item")
}
日志打幽擅ā:
2020-08-24 01:06:41.200 21911-21911/com.tomorrow.kotlindemo D/TAG: zwm, index: 0, item: Java
2020-08-24 01:06:41.200 21911-21911/com.tomorrow.kotlindemo D/TAG: zwm, index: 1, item: Android
還可以對(duì)map應(yīng)用過濾和變換函數(shù),map的鍵和值分別由各自的函數(shù)來處理竹捉,filterKeys和mapKeys過濾和變換map的鍵续担,而另外的filterValues和mapValues過濾和變換對(duì)應(yīng)的值。
fun method() {
val numbers = mapOf(0 to "zero", 1 to "one", 2 to "two")
Log.d("TAG", "zwm, filterKeys: ${numbers.filterKeys { it >= 1 }}")
Log.d("TAG", "zwm, mapKeys: ${numbers.mapKeys { it.key + 10 }}")
Log.d("TAG", "zwm, filterValues: ${numbers.filterValues { it == "two" }}")
Log.d("TAG", "zwm, mapValues: ${numbers.mapValues { it.value + "_map" }}")
}
日志打踊詈ⅰ:
2020-08-24 01:25:09.827 25896-25896/? D/TAG: zwm, filterKeys: {1=one, 2=two}
2020-08-24 01:25:09.829 25896-25896/? D/TAG: zwm, mapKeys: {10=zero, 11=one, 12=two}
2020-08-24 01:25:09.829 25896-25896/? D/TAG: zwm, filterValues: {2=two}
2020-08-24 01:25:09.829 25896-25896/? D/TAG: zwm, mapValues: {0=zero_map, 1=one_map, 2=two_map}
"all" "any" "count" 和 "find":對(duì)集合應(yīng)用判斷式
對(duì)是否所有元素都滿足判斷式感興趣,應(yīng)該使用all函數(shù):
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Kotlin", 5), Person("Java", 20), Person("Android", 10))
val result = list.all { it.age >= 5 }
Log.d("TAG", "zwm, result: $result")
}
日志打庸猿稹:
2020-08-24 01:34:13.967 26347-26347/com.tomorrow.kotlindemo D/TAG: zwm, result: true
需要檢查集合中是否至少存在一個(gè)匹配的元素憾儒,那就用any:
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Kotlin", 5), Person("Java", 20), Person("Android", 10))
val result = list.any { it.age >= 20 }
Log.d("TAG", "zwm, result: $result")
}
日志打印:
2020-08-24 01:36:13.500 26662-26662/com.tomorrow.kotlindemo D/TAG: zwm, result: true
!all(不是所有)加上某個(gè)條件乃沙,可以用any加上這個(gè)條件的取反來替換:
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Kotlin", 5), Person("Java", 20), Person("Android", 10))
val result = !list.all { it.age >= 20 }
Log.d("TAG", "zwm, result: $result")
}
日志打悠鹬骸:
2020-08-24 01:39:54.101 27422-27422/com.tomorrow.kotlindemo D/TAG: zwm, result: true
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Kotlin", 5), Person("Java", 20), Person("Android", 10))
val result = list.any { it.age < 20 }
Log.d("TAG", "zwm, result: $result")
}
日志打印:
2020-08-24 01:41:00.679 27898-27898/? D/TAG: zwm, result: true
想知道有多少個(gè)元素滿足了判斷式警儒,使用count:
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Kotlin", 5), Person("Java", 20), Person("Android", 10))
val result = list.count { it.age < 20 }
Log.d("TAG", "zwm, result: $result")
}
日志打友雕伞:
2020-08-24 01:43:00.513 28113-28113/com.tomorrow.kotlindemo D/TAG: zwm, result: 2
要找到一個(gè)滿足判斷式的元素眶根,使用find函數(shù):
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Kotlin", 5), Person("Java", 20), Person("Android", 10))
val result = list.find { it.age < 20 }
Log.d("TAG", "zwm, result: $result")
}
日志打印:
2020-08-24 01:45:23.639 28734-28734/com.tomorrow.kotlindemo D/TAG: zwm, result: Kotlin
如果有多個(gè)匹配的元素就返回其中第一個(gè)元素边琉,如果沒有一個(gè)元素能滿足判斷式則返回null属百。find還有一個(gè)同義方法firstOrNull,可以使用這個(gè)方法更清楚地表述你的意圖变姨。
groupBy:把列表轉(zhuǎn)換成分組的map
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Kotlin", 5), Person("Java", 20), Person("Android", 10))
val result = list.groupBy { it.age }
for((key, value) in result) {
Log.d("TAG", "zwm, key: $key")
for((index, item) in value.withIndex()) {
Log.d("TAG", "zwm, index: $index, name: ${item.name}")
}
}
}
日志打幼迦拧:
2020-08-24 01:57:13.505 30160-30160/com.tomorrow.kotlindemo D/TAG: zwm, key: 5
2020-08-24 01:57:13.506 30160-30160/com.tomorrow.kotlindemo D/TAG: zwm, index: 0, name: Kotlin
2020-08-24 01:57:13.506 30160-30160/com.tomorrow.kotlindemo D/TAG: zwm, key: 20
2020-08-24 01:57:13.506 30160-30160/com.tomorrow.kotlindemo D/TAG: zwm, index: 0, name: Java
2020-08-24 01:57:13.506 30160-30160/com.tomorrow.kotlindemo D/TAG: zwm, key: 10
2020-08-24 01:57:13.506 30160-30160/com.tomorrow.kotlindemo D/TAG: zwm, index: 0, name: Android
flatMap和flatten:處理嵌套集合中的元素
flatMap函數(shù)做了兩件事情:首先根據(jù)作為實(shí)參給定的函數(shù)對(duì)集合中的每個(gè)元素做變換(或者說映射),然后把多個(gè)列表合并(或者說平鋪)成一個(gè)列表:
fun method() {
val list = listOf("abc", "def")
val result = list.flatMap { it.toList() }
Log.d("TAG", "zwm, result: $result")
}
日志打佣ㄅ贰:
2020-08-24 02:06:29.554 30522-30522/com.tomorrow.kotlindemo D/TAG: zwm, result: [a, b, c, d, e, f]
如果你不需要做任何變換渔呵,只是需要平鋪一個(gè)集合,可以使用flatten函數(shù):
fun method() {
val list1 = listOf("abc", "def")
val list2 = listOf("ghi", "jkl")
val list = listOf(list1, list2)
val result = list.flatten()
Log.d("TAG", "zwm, result: $result")
}
日志打涌仇:
2020-08-24 02:12:29.813 31375-31375/com.tomorrow.kotlindemo D/TAG: zwm, result: [abc, def, ghi, jkl]
3.惰性集合操作:序列
Kotlin標(biāo)準(zhǔn)庫(kù)參考文檔有說明:filter和map都會(huì)返回一個(gè)列表扩氢。這意味著使用filter和map的鏈?zhǔn)秸{(diào)用會(huì)創(chuàng)建兩個(gè)列表:一個(gè)保存filter函數(shù)的結(jié)果,另一個(gè)保存map函數(shù)的結(jié)果爷辱。如果源列表只有兩個(gè)元素录豺,這不是什么問題,但是如果有一百萬個(gè)元素托嚣,鏈?zhǔn)秸{(diào)用就會(huì)變得十分低效巩检。為了提高效率,可以把操作變成使用序列示启,而不是直接使用集合兢哭,這樣就不會(huì)創(chuàng)建任何用于存儲(chǔ)元素的中間集合:
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Apple", 5), Person("Billy", 20), Person("Android", 10))
val result = list.asSequence() //把初始集合轉(zhuǎn)換成序列
.map(Person::name)
.filter { it.startsWith("A") }
.toList() //把結(jié)果序列轉(zhuǎn)換回列表
Log.d("TAG", "zwm, result: $result")
}
日志打印:
2020-08-24 02:32:16.517 973-973/? D/TAG: zwm, result: [Apple, Android]
Kotlin惰性集合操作的入口就是Sequence接口夫嗓,這個(gè)接口表示的就是一個(gè)可以逐個(gè)列舉元素的元素序列迟螺。Sequence只提供了一個(gè)方法,iterator舍咖,用來從序列中獲取值矩父。可以調(diào)用擴(kuò)展函數(shù)asSequence把任意集合轉(zhuǎn)換成序列排霉,調(diào)用toList來做反向的轉(zhuǎn)換窍株。
執(zhí)行序列操作:中間和末端操作
class Person(val name: String, val age: Int)
fun method() {
val list = listOf(Person("Apple", 5), Person("Billy", 20), Person("Android", 10))
val result = list.asSequence()
.map(Person::name) //中間操作
.filter { it.startsWith("A") } //中間操作
.toList() //末端操作
Log.d("TAG", "zwm, result: $result")
}
對(duì)序列來說,所有操作是按順序應(yīng)用在每一個(gè)元素上:處理完第一個(gè)元素(先映射再過濾)攻柠,然后完成第二個(gè)元素的處理球订,以此類推。
創(chuàng)建序列
在集合上調(diào)用asSequence()可以創(chuàng)建一個(gè)序列瑰钮,另外也可以使用generateSequence函數(shù)冒滩。給定序列中的前一個(gè)元素,這個(gè)函數(shù)會(huì)計(jì)算出下一個(gè)元素:
fun method() {
val naturalNumbers = generateSequence(0) { it + 1 }
val numbersTo100 = naturalNumbers.takeWhile { it <= 100 }
val result = numbersTo100.sum() //當(dāng)獲取結(jié)果時(shí)浪谴,所有被推遲的操作都被執(zhí)行
Log.d("TAG", "zwm, result: $result")
}
日志打涌:
2020-08-24 02:58:10.859 3913-3913/com.tomorrow.kotlindemo D/TAG: zwm, result: 5050
4.使用Java函數(shù)式接口
把lambda當(dāng)做參數(shù)傳遞給Java方法:
button.setOnClickListener { Log.d("TAG", "zwm, focusable: ${it.focusable}") }
這種方式可以工作的原因是OnClickListener接口只有一個(gè)抽象方法因苹。這種接口被稱為函數(shù)式接口,或者SAM接口篇恒,SAM代表單抽象方法扶檐。Java API中隨處可見像Runnable和Callable這樣的函數(shù)式接口,以及支持它們的方法婚度。Kotlin允許你在調(diào)用接收函數(shù)式接口作為參數(shù)的方法時(shí)使用lambda蘸秘,來保證你的Kotlin代碼既整潔又符合習(xí)慣。
使用匿名對(duì)象:
button.setOnClickListener { object : Runnable {
override fun run() {
Log.d("TAG", "zwm, focusable: ${it.focusable}")
}
} }
使用匿名對(duì)象時(shí)蝗茁,每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的實(shí)例醋虏。而使用lambda時(shí),如果lambda沒有訪問任何來自定義它的函數(shù)的變量哮翘,相應(yīng)的匿名類實(shí)例可以在多次調(diào)用之間重用颈嚼;如果lambda從包圍它的作用域中捕捉了變量,每次調(diào)用就不再可能重用同一個(gè)實(shí)例了饭寺,這種情況下阻课,每次調(diào)用時(shí)編譯器都要?jiǎng)?chuàng)建一個(gè)新對(duì)象,其中存儲(chǔ)著被捕捉的變量的值艰匙。
method(1000) { println(42) } //整個(gè)程序只會(huì)創(chuàng)建一個(gè)實(shí)例
fun outMethod(id: String) {
method(1000) { println(id) } //每次調(diào)用都會(huì)創(chuàng)建一個(gè)新實(shí)例
}
Lambda的實(shí)現(xiàn)細(xì)節(jié)
自Kotlin 1.0起限煞,每個(gè)lambda表達(dá)式都會(huì)被編譯成一個(gè)匿名類,除非它是一個(gè)內(nèi)聯(lián)lambda员凝。如果lambda捕捉了變量署驻,每個(gè)被捕捉的變量會(huì)在匿名類中有對(duì)應(yīng)的字段,而且每次對(duì)lambda的調(diào)用都會(huì)創(chuàng)建一個(gè)這個(gè)匿名類的新實(shí)例健霹。否則旺上,一個(gè)單例就會(huì)被創(chuàng)建。
SAM構(gòu)造方法:顯示地把lambda轉(zhuǎn)換成函數(shù)式接口
SAM構(gòu)造方法是編譯器生成的函數(shù)糖埋,讓你執(zhí)行從lambda到函數(shù)式接口實(shí)例的顯示轉(zhuǎn)換宣吱。可以在編譯器不會(huì)自動(dòng)應(yīng)用轉(zhuǎn)換的上下文中使用它瞳别。例如征候,如果有一個(gè)方法返回的是一個(gè)函數(shù)式接口的實(shí)例,不能直接返回一個(gè)lambda祟敛,要用SAM構(gòu)造方法把它包裝起來:
fun createAllDoneRunnable(): Runnable {
return Runnable { println("All done!") }
}
fun method() {
createAllDoneRunnable().run()
}
SAM構(gòu)造方法的名稱和底層函數(shù)式接口的名稱一樣倍奢。SAM構(gòu)造方法只接收一個(gè)參數(shù):一個(gè)被用作函數(shù)式接口單抽象方法體的lambda,并返回實(shí)現(xiàn)了這個(gè)接口的類的一個(gè)實(shí)例垒棋。
除了返回值外,SAM構(gòu)造方法還可以用在需要把從lambda生成的函數(shù)式接口實(shí)例存儲(chǔ)在一個(gè)變量中的情況:
val listener = OnClickListener { view -> //使用SAM構(gòu)造方法來重用listener實(shí)例
val text = when (view.id) { //根據(jù)view.id來判斷點(diǎn)擊的是哪個(gè)按鈕
R.id.button1 -> "First button"
R.id.button2 -> "Second button"
else -> "Unknown button"
}
toast(text)
}
button1.setOnClickListener(listener)
button2.setOnClickListener(listener)
Lambda和添加/移除監(jiān)聽器
注意lambda內(nèi)部沒有匿名對(duì)象那樣的this:沒有辦法引用到lambda轉(zhuǎn)換成的匿名類實(shí)例痪宰。從編譯器的角度來看叼架,lambda是一個(gè)代碼塊畔裕,不是一個(gè)對(duì)象,而且也不能把它當(dāng)成對(duì)象引用乖订。Lambda中的this引用指向的是包圍它的類扮饶。如果你的事件監(jiān)聽器在處理事件時(shí)還需要取消它自己,不能使用lambda這樣做乍构。這種情況使用實(shí)現(xiàn)了接口的匿名對(duì)象甜无。在匿名對(duì)象內(nèi),this關(guān)鍵字指向該對(duì)象實(shí)例哥遮,可以把它傳給移除監(jiān)聽器的API岂丘。
5.帶接收者的lambda:with與apply
with函數(shù):
fun method() {
val stringBuilder = StringBuilder()
val result = with(stringBuilder) { //指定接收者的值,你會(huì)調(diào)用它的方法
for (letter in 'A'..'Z') {
this.append(letter) //通過顯式的this來調(diào)用接收者值的方法
}
append("done") //省掉this也可以調(diào)用接收者值的方法
this.toString()
}
Log.d("TAG", "zwm, result: $result")
}
日志打用咭:
2020-08-24 15:14:55.028 1709-1709/com.tomorrow.kotlindemo D/TAG: zwm, result: ABCDEFGHIJKLMNOPQRSTUVWXYZdone
with結(jié)構(gòu)看起來像是一種特殊的語法結(jié)構(gòu)奥帘,但它實(shí)際上是一個(gè)接收兩個(gè)參數(shù)的函數(shù):以上例子中兩個(gè)參數(shù)分別是stringBuilder和一個(gè)lambda。這里利用了把lambda放在括號(hào)外的約定仪召,這樣整個(gè)調(diào)用看起來就像是內(nèi)建的語言功能寨蹋。with函數(shù)把它的第一個(gè)參數(shù)轉(zhuǎn)換成作為第二個(gè)參數(shù)傳給它的lambda的接收者∪用可以顯式地通過this引用來訪問這個(gè)接收者已旧,也可以省略this引用,不用任何限定符直接訪問這個(gè)值的方法和屬性召娜。
帶接收者的lambda和擴(kuò)展函數(shù)
在擴(kuò)展函數(shù)體內(nèi)部运褪,this指向了這個(gè)函數(shù)擴(kuò)展的那個(gè)類型的實(shí)例,而且也可以被省略掉萤晴,讓你直接訪問接收者的成員吐句。注意一個(gè)擴(kuò)展函數(shù)某種意義上來說就是帶接收者的函數(shù)。
方法名稱沖突:
class Outer(val name: String) {
fun method() {
val stringBuilder = StringBuilder()
val result = with(stringBuilder) {
append(this@Outer.toString()) //使用this@Outer引用外部類實(shí)例
append("done")
this.toString() //使用this引用接收者實(shí)例
}
Log.d("TAG", "zwm, result: $result")
}
override fun toString(): String {
return "I am Outer"
}
}
日志打拥甓痢:
2020-08-24 15:37:16.150 3594-3594/com.tomorrow.kotlindemo D/TAG: zwm, result: I am Outerdone
apply函數(shù):
with函數(shù)返回的值是執(zhí)行了lambda代碼的結(jié)果嗦枢,該結(jié)果就是lambda中的最后一個(gè)表達(dá)式的值。apply函數(shù)幾乎和with函數(shù)一模一樣屯断,唯一的區(qū)別是apply始終會(huì)返回作為實(shí)參傳遞給它的對(duì)象(即接收者對(duì)象)文虏。
fun method() {
val stringBuilder = StringBuilder()
val result = stringBuilder.apply {
for (letter in 'A'..'Z') {
this.append(letter)
}
append("done")
this.toString()
}.toString()
Log.d("TAG", "zwm, result: $result")
}
日志打忧⒃恪:
2020-08-24 15:43:16.246 4227-4227/com.tomorrow.kotlindemo D/TAG: zwm, result: ABCDEFGHIJKLMNOPQRSTUVWXYZdone
apply被聲明成一個(gè)擴(kuò)展函數(shù)末贾,它的接收者變成了作為實(shí)參的lambda的接收者锦秒。在Kotlin中绕沈,可以在任意對(duì)象上使用apply骡湖,完全不需要任何來自定義該對(duì)象的庫(kù)的特別支持廷臼。
with函數(shù)和apply函數(shù)是最基本和最通用的使用帶接收者的lambda的例子拌蜘,更多具體的函數(shù)也可以使用這種模式竟秫。例如彼棍,可以使用標(biāo)準(zhǔn)庫(kù)函數(shù)buildString灭忠,它會(huì)負(fù)責(zé)創(chuàng)建StringBuilder并調(diào)用toString膳算。buildString的實(shí)參是一個(gè)帶接收者的lambda,接收者就是StringBuilder:
fun method() {
val result = buildString {
for (letter in 'A'..'Z') {
append(letter)
}
append("done")
}
Log.d("TAG", "zwm, result: $result")
}
日志打映谧鳌:
2020-08-24 15:55:52.341 4814-4814/com.tomorrow.kotlindemo D/TAG: zwm, result: ABCDEFGHIJKLMNOPQRSTUVWXYZdone
六涕蜂、Kotlin的類型系統(tǒng)
1.可空性
Kotlin的類型結(jié)構(gòu):
Kotlin和Java的類型系統(tǒng)之間第一條也可能是最重要的一條區(qū)別是,Kotlin對(duì)可空類型的顯式的支持映琳。問號(hào)可以加在任何類型的后面來表示這個(gè)類型的變量可以存儲(chǔ)null引用机隙,例如:String?、Int?萨西、MyCustomType? 等等有鹿。
Type? = Type or null
沒有問號(hào)的類型表示這種類型的變量不能存儲(chǔ)null引用。這說明所有常見類型默認(rèn)都是非空的原杂,除非顯式地把它標(biāo)記為可空印颤。
一旦你有一個(gè)可空類型的值,能對(duì)它進(jìn)行的操作也會(huì)受到限制:
fun method(str: String?) {
val length = str.length //編譯錯(cuò)誤穿肄,str可能為null
val x: String? = null //正確年局,x可以為null
val y: String = x //編譯錯(cuò)誤,y不可以為null
}
安全調(diào)用運(yùn)算符:咸产?矢否,允許你把一次null檢查和一次調(diào)用合并成一個(gè)操作:
fun method() {
val str : String? = null
val length = str?.length //訪問屬性
val ch = str?.get(0) //調(diào)用方法
Log.d("TAG", "zwm, length: $length, ch: $ch")
}
日志打印:
2020-08-24 19:50:28.066 25302-25302/com.tomorrow.kotlindemo D/TAG: zwm, length: null, ch: null
Elvis運(yùn)算符:?:脑溢,也叫做null合并運(yùn)算符僵朗,接收兩個(gè)運(yùn)算數(shù),如果第一個(gè)運(yùn)算數(shù)不為null屑彻,運(yùn)算結(jié)果就是第一個(gè)運(yùn)算數(shù)验庙,如果第一個(gè)運(yùn)算數(shù)為null,運(yùn)算結(jié)果就是第二個(gè)運(yùn)算數(shù):
fun method() {
val str : String? = null
val length = str?.length ?: 0
Log.d("TAG", "zwm, length: $length")
}
日志打由缟:
2020-08-24 20:04:06.707 2443-2443/com.tomorrow.kotlindemo D/TAG: zwm, length: 0
安全轉(zhuǎn)換:as粪薛?,嘗試把值轉(zhuǎn)換成指定的類型搏恤,如果值不是合適的類型就返回null:
class Person(val name: String, val age: Int)
fun method() {
val str: String? = "hello"
val result = str as? Person
Log.d("TAG", "zwm, result: $result")
}
日志打游ナ佟:
2020-08-24 21:03:33.696 18222-18222/com.tomorrow.kotlindemo D/TAG: zwm, result: null
非空斷言:!!,使用雙感嘆號(hào)表示熟空,可以把任何值轉(zhuǎn)換成非空類型藤巢,如果對(duì)null值做非空斷言,則會(huì)拋出異常:
fun method() {
val str: String? = null
val result = str!!.length
Log.d("TAG", "zwm, result: $result")
}
日志打酉⒙蕖:
kotlin.KotlinNullPointerException
let函數(shù)掂咒,讓處理可空表達(dá)式變得更容易。和安全調(diào)用運(yùn)算符一起,它允許你對(duì)表達(dá)式求值绍刮,檢查求值結(jié)果是否為null糜工,并把結(jié)果保存為一個(gè)變量,所有這些動(dòng)作都在同一個(gè)簡(jiǎn)潔的表達(dá)式中:
fun method() {
val str: String? = "Kotlin"
str?.let { Log.d("TAG", "zwm, value: $it") }
}
日志打勇嫉:
2020-08-24 21:48:55.457 25465-25465/com.tomorrow.kotlindemo D/TAG: zwm, value: Kotlin
延遲初始化的屬性
延遲初始化的屬性都是var,因?yàn)樾枰跇?gòu)造方法外修改它的值油坝,而val屬性會(huì)被編譯成必須在構(gòu)造方法中初始化的final字段:
class Person(val name: String) {
lateinit var parent: Person //延遲初始化屬性
fun initPerson() {
val father = Person("Father")
parent = father //延遲初始化
Log.d("TAG", "zwm, name: ${name} ${parent.name}")
}
}
fun method() {
val person = Person("Happy")
person.initPerson()
}
日志打蛹灯荨:
2020-08-24 22:09:14.567 26769-26769/com.tomorrow.kotlindemo D/TAG: zwm, name: Happy Father
可空類型的擴(kuò)展
為可空類型定義擴(kuò)展函數(shù)是一種更強(qiáng)大的處理null值的方式〕喝Γ可以允許接收者為null的擴(kuò)展函數(shù)調(diào)用彬檀,并在該函數(shù)中處理null,而不是在確保變量不為null之后再調(diào)用它的方法瞬女。只有擴(kuò)展函數(shù)才能做到這一點(diǎn)窍帝,普通成員方法的調(diào)用是通過對(duì)象實(shí)例來分發(fā)的,因此實(shí)例為null時(shí)成員方法永遠(yuǎn)不能被執(zhí)行诽偷。
函數(shù)isEmptyOrNull和isNullOrBlank就可以由String?類型的接收者調(diào)用:(不需要安全調(diào)用@ぱА)
fun method() {
val str: String? = " "
val str2: String? = null
Log.d("TAG", "zwm, ${str.isNullOrBlank()} ${str.isNullOrEmpty()}")
Log.d("TAG", "zwm, ${str2.isNullOrBlank()} ${str2.isNullOrEmpty()}")
}
日志打印:
2020-08-24 22:40:13.402 28472-28472/com.tomorrow.kotlindemo D/TAG: zwm, true false
2020-08-24 22:40:13.402 28472-28472/com.tomorrow.kotlindemo D/TAG: zwm, true true
fun String?.isNullOrBlank(): Boolean = //可空字符串的擴(kuò)展
this == null || this.isBlank() //第二個(gè)this使用了智能轉(zhuǎn)換
當(dāng)你為一個(gè)可空類型(以?結(jié)尾)定義擴(kuò)展函數(shù)時(shí)报慕,這意味著你可以對(duì)可空的值調(diào)用這個(gè)函數(shù)深浮,并且函數(shù)體中的this可能為null,所以你必須顯式地檢查眠冈。
類型參數(shù)的可空性
Kotlin中所有泛型類和泛型函數(shù)的類型參數(shù)默認(rèn)都是可空的飞苇。任何類型,包括可空類型在內(nèi)蜗顽,都可以替換類型參數(shù)布卡。這種情況下,使用類型參數(shù)作為類型的聲明都允許為null雇盖,盡管類型參數(shù)T并沒有用問號(hào)結(jié)尾:
fun <T> printHashCode(t: T) { //類型參數(shù)T推導(dǎo)出的類型是可空類型Any?
Log.d("TAG", "zwm, ${t?.hashCode()}") //因?yàn)閠可能為null忿等,所以必須使用安全調(diào)用
}
fun method() {
printHashCode("abc")
}
日志打印:
2020-08-24 23:04:13.388 30688-30688/com.tomorrow.kotlindemo D/TAG: zwm, 96354
要使類型參數(shù)非空刊懈,必須要為它指定一個(gè)非空的上界这弧,那樣泛型會(huì)拒絕可空值作為實(shí)參:
fun <T: Any> printHashCode(t: T) { //類型參數(shù)T推導(dǎo)出的類型是可空類型Any
Log.d("TAG", "zwm, ${t.hashCode()}") //因?yàn)閠不可能為null,所以不需要使用安全調(diào)用
}
fun method() {
printHashCode("abc")
}
日志打有檠础:
2020-08-24 23:09:11.924 31193-31193/com.tomorrow.kotlindemo D/TAG: zwm, 96354
注意必須使用問號(hào)結(jié)尾來標(biāo)記類型為可空的匾浪,沒有問號(hào)就是非空的。類型參數(shù)是這個(gè)規(guī)則唯一的例外卷哩。
可空性和Java
Java中的@Nullable String被Kotlin當(dāng)做String?蛋辈,而@NotNull String就是String。
Java類型在Kotlin中表示為平臺(tái)類型,既可以把它當(dāng)作可空類型也可以當(dāng)作非空類型來處理冷溶。這意味著渐白,你要像在Java中一樣,對(duì)你在這個(gè)類型上做的操作負(fù)有全部責(zé)任逞频。
當(dāng)通過繼承在Kotlin中重寫Java的方法時(shí)纯衍,可以選擇把參數(shù)和返回類型定義成可空的,也可以選擇把它們定義成非空的苗胀。注意襟诸,在實(shí)現(xiàn)Java類或者接口的方法時(shí)一定要搞清楚它的可空性,因?yàn)榉椒ǖ膶?shí)現(xiàn)可以在非Kotlin的代碼中被調(diào)用基协。
2.基本數(shù)據(jù)類型和其他基本類型
基本數(shù)據(jù)類型:Int歌亲、Boolean及其他
Kotlin并不區(qū)分基本數(shù)據(jù)類型和包裝類型,你使用的永遠(yuǎn)是同一個(gè)類型澜驮。大多數(shù)情況下陷揪,對(duì)于變量、屬性杂穷、參數(shù)和返回類型悍缠,Kotlin的Int類型會(huì)被編譯成Java基本數(shù)據(jù)類型int。唯一不可行的例外是泛型類亭畜,比如集合扮休,用作泛型類型參數(shù)的基本數(shù)據(jù)類型會(huì)被編譯成對(duì)應(yīng)的Java包裝類型,Kotlin的Int類型會(huì)被編譯成Java包裝類型java.lang.Integer拴鸵。
val i: Int = 1 //編譯成基本數(shù)據(jù)類型int
val list: List<Int> = listOf(1, 2, 3) //編譯成包裝類型java.lang.Integer
可空的基本數(shù)據(jù)類型:Int?玷坠、Boolean?及其他
Kotlin中的可空類型不能用Java的基本數(shù)據(jù)類型表示,因?yàn)閚ull只能被存儲(chǔ)在Java的引用類型的變量中劲藐。這意味著任何時(shí)候只要使用了基本數(shù)據(jù)類型的可空版本八堡,它就會(huì)編譯成對(duì)應(yīng)的包裝類型。
val i: Int? = 1 //編譯成包裝類型java.lang.Integer
數(shù)字轉(zhuǎn)換
Kotlin不會(huì)自動(dòng)地把數(shù)字從一種類型轉(zhuǎn)換成另外一種聘芜,即便是轉(zhuǎn)換成范圍更大的類型兄渺,Kotlin要求轉(zhuǎn)換必須是顯式的:
val i = 1
val l: Long = i //編譯錯(cuò)誤:類型不匹配
val l: Long = i.toLong() //正確
對(duì)應(yīng)到Java基本數(shù)據(jù)類型的類型完整列表如下:
整數(shù)類型:Byte、Short汰现、Int挂谍、Long
浮點(diǎn)數(shù)類型:Float、Double
字符類型:Char
布爾類型:Boolean
基本數(shù)據(jù)類型字面值:
Long:123L
Double:0.12瞎饲、2.0口叙、1.2e10、1.2e-10
Float:123.4f嗅战、.456F妄田、1e3f
十六機(jī)制:0xCAFEBABE俺亮、0xbcdL
二進(jìn)制:0b000000101
字符串轉(zhuǎn)換
Kotlin標(biāo)準(zhǔn)庫(kù)提供了一套相似的擴(kuò)展方法,用來把字符串轉(zhuǎn)換成基本數(shù)據(jù)類型:toInt疟呐、toByte脚曾、toBoolean等,每個(gè)這樣的函數(shù)都會(huì)嘗試把字符串的內(nèi)容解析成對(duì)應(yīng)的類型启具,如果解析失敗則拋出NumberFormatException本讥。
Any和Any?:根類型
Any類型是Kotlin所有非空類型的超類型。當(dāng)Kotlin函數(shù)使用Any時(shí)鲁冯,它會(huì)被編譯成Java字節(jié)碼中的Object囤踩。所有Kotlin類都包含下面三個(gè)方法:toString、equals和hashCode晓褪,這些方法都繼承自Any。Any并不能使用其他java.lang.Object的方法(比如wait和notify)综慎,但是可以通過手動(dòng)把值轉(zhuǎn)換成java.lang.Object來調(diào)用這些方法涣仿。
Unit類型:Kotlin的void
Unit是一個(gè)完備的類型,可以作為類型參數(shù)示惊,而void卻不行好港。只存在一個(gè)值是Unit類型,這個(gè)值也叫作Unit米罚,并且在函數(shù)中會(huì)被隱式地返回钧汹。當(dāng)你在重寫返回泛型參數(shù)的函數(shù)時(shí)這非常有用,只需要讓方法返回Unit類型的值:
interface Processor<T> {
fun process(): T
}
class NoResultProcessor : Processor<Unit> { //使用Unit作為類型參數(shù)
override fun process() { //返回Unit录择,但可以省略類型說明
//do stuff //這里不需要顯式的return
}
}
Nothing類型:這個(gè)函數(shù)永不返回
Nothing類型沒有任何值拔莱,只有被當(dāng)作函數(shù)返回值使用,或者被當(dāng)作泛型函數(shù)返回值的類型參數(shù)使用才會(huì)有意義隘竭。在其他所有情況下塘秦,聲明一個(gè)不能存儲(chǔ)任何值的變量沒有任何意義。
fun fail(message: String): Nothing {
throw IllegalStateException(message)
}
fun method() {
fail("I am fail")
}
日志打佣础:
java.lang.IllegalStateException: I am fail
3.集合與數(shù)組
Kotlin以Java集合庫(kù)為基礎(chǔ)構(gòu)建尊剔,并通過擴(kuò)展函數(shù)增加的特性來增強(qiáng)它。
集合的繼承關(guān)系:
可空性和集合
Kotlin支持類型參數(shù)的可空性:
fun method() {
val list = ArrayList<Int?>()
list.add(999)
list.add(666)
list.add(888)
list.add(null)
Log.d("TAG", "zwm, list: $list")
}
日志打恿饨浴:
2020-08-25 02:04:55.476 9146-9146/com.tomorrow.kotlindemo D/TAG: zwm, list: [999, 666, 888, null]
聲明一個(gè)變量持有可空的列表须误,并且包含可空的數(shù)字:List<Int?>?,有兩個(gè)問號(hào)仇轻,使用變量自己的值的時(shí)候京痢,以及使用列表中每個(gè)元素的值的時(shí)候,都需要使用null檢查拯田。
Kotlin提供了一個(gè)標(biāo)準(zhǔn)庫(kù)函數(shù)filterNotNull用來遍歷一個(gè)包含可空值的集合并過濾掉null历造,這種過濾也影響了集合的類型:
fun method() {
val list = ArrayList<Int?>()
list.add(999)
list.add(666)
list.add(888)
list.add(null)
Log.d("TAG", "zwm, list: ${list.filterNotNull()}")
}
日志打印:
2020-08-25 02:09:59.072 9852-9852/com.tomorrow.kotlindemo D/TAG: zwm, list: [999, 666, 888]
只讀集合與可變集合:
Kotlin把訪問集合數(shù)據(jù)的接口和修改集合數(shù)據(jù)的接口分開了,kotlin.collections.Collection接口可以遍歷集合中的元素、獲取集合大小颁督、判斷集合中是否包含某個(gè)元素劈彪,以及執(zhí)行其他從該集合中讀取數(shù)據(jù)的操作。但這個(gè)接口沒有任何添加或移除元素的方法橄霉。
使用kotlin.collections.MutableCollection接口可以修改集合中的數(shù)據(jù)邑蒋。它繼承了普通的kotlin.collections.Collection接口医吊,還提供了方法來添加和移除元素饿敲、清空集合等沥曹。
fun <T> copyElements(source: Collection<T>, target: MutableCollection<T>) {
for(item in source) {
target.add(item)
}
}
fun method() {
val source: Collection<Int> = arrayListOf(3, 5, 7)
val target: MutableCollection<Int> = arrayListOf(1)
copyElements(source, target)
Log.d("TAG", "zwm, target: $target")
}
日志打恿鸲怠:
2020-08-25 02:24:42.891 10690-10690/com.tomorrow.kotlindemo D/TAG: zwm, target: [1, 3, 5, 7]
使用集合接口時(shí)需要牢記的一個(gè)關(guān)鍵點(diǎn)是只讀集合不一定是不可變的该互。如果你使用的變量擁有一個(gè)只讀接口類型隘马,它可能只是同一個(gè)集合的眾多引用中的一個(gè)钝鸽,任何其他的引用都可能擁有一個(gè)可變接口類型风皿。
Kotlin集合和Java
集合創(chuàng)建函數(shù):
集合類型 | 只讀 | 可變 |
---|---|---|
List | listOf | mutableListOf袋励、arrayListOf |
Set | setOf | mutableSetOf、hashSetOf吴旋、linkedSetOf项棠、sortedSetOf |
Map | mapOf | mutableMapOf峭咒、hashMapOf凑队、linkedMapOf则果、sortedMapOf |
Java并不會(huì)區(qū)分只讀集合與可變集合,即使Kotlin中把集合聲明成只讀的顽决,Java代碼也能夠修改這個(gè)集合。
作為平臺(tái)類型的集合
Kotlin把那些定義在Java代碼中的類型看成平臺(tái)類型导匣,Kotlin沒有任何關(guān)于平臺(tái)類型的可空性信息才菠,所以編譯器允許Kotlin代碼將其視為可空或非空。同樣贡定,Java中聲明的集合類型的變量也被視為平臺(tái)類型赋访,一個(gè)平臺(tái)類型的集合本質(zhì)上就是可變性未知的集合,Kotlin代碼將其視為只讀的或者可變的缓待。
對(duì)象和基本數(shù)據(jù)類型的數(shù)組
要在Kotlin中創(chuàng)建數(shù)組蚓耽,有下面這些方法:
- arrayOf函數(shù)創(chuàng)建一個(gè)數(shù)組,它包含的元素是指定為該函數(shù)的實(shí)參旋炒。
- arrayOfNulls創(chuàng)建一個(gè)給定大小的數(shù)組步悠,包含的是null元素。當(dāng)然瘫镇,它只能用來創(chuàng)建包含元素類型可空的數(shù)組鼎兽。
- Array構(gòu)造方法接收數(shù)組的大小和一個(gè)lambda表達(dá)式答姥,調(diào)用lambda表達(dá)式來創(chuàng)建每一數(shù)組元素。這就是使用非空元素類型來初始化數(shù)組谚咬,但不用顯式地傳遞每個(gè)元素的方式鹦付。
Kotlin中的數(shù)組是支持泛型的,當(dāng)然也不再協(xié)變择卦,也就是說你不能將任意一個(gè)對(duì)象數(shù)組賦值給Array<Any>或者Array<Any?>敲长。
fun method() {
val arr = arrayOf(3, 5, 7)
val arr2 = arrayOfNulls<Int>(3)
val arr3 = Array(3) { 100 + it }
for (i in arr.indices) {
Log.d("TAG", "zwm, ${arr[i]}")
}
for (i in arr2.indices) {
Log.d("TAG", "zwm, ${arr2[i]}")
}
for (i in arr3.indices) {
Log.d("TAG", "zwm, ${arr3[i]}")
}
}
日志打印:
2020-08-25 03:11:50.443 14918-14918/? D/TAG: zwm, 3
2020-08-25 03:11:50.443 14918-14918/? D/TAG: zwm, 5
2020-08-25 03:11:50.443 14918-14918/? D/TAG: zwm, 7
2020-08-25 03:11:50.443 14918-14918/? D/TAG: zwm, null
2020-08-25 03:11:50.443 14918-14918/? D/TAG: zwm, null
2020-08-25 03:11:50.444 14918-14918/? D/TAG: zwm, 100
2020-08-25 03:11:50.444 14918-14918/? D/TAG: zwm, 101
2020-08-25 03:11:50.444 14918-14918/? D/TAG: zwm, 102
向vararg方法傳遞集合:
fun method() {
val strings = listOf("a", "b", "c")
val result = format("%s/%s/%s", *strings.toTypedArray()) //期望vararg參數(shù)時(shí)使用展開運(yùn)算符(*)傳遞數(shù)組
Log.d("TAG", "zwm, result: $result")
}
日志打颖獭:
2020-08-25 03:17:48.876 15174-15174/com.tomorrow.kotlindemo D/TAG: zwm, result: a/b/c
數(shù)組類型的類型參數(shù)始終會(huì)變成對(duì)象類型祈噪。因此,如果你聲明了一個(gè)Array<Int>秕噪,它將會(huì)是一個(gè)包含裝箱整型的數(shù)組(它的Java類型將是java.lang.Integer[])钳降。如果你需要?jiǎng)?chuàng)建沒有裝箱的基本數(shù)據(jù)類型的數(shù)組,必須使用一個(gè)基本數(shù)據(jù)類型數(shù)組的特殊類腌巾。
為了表示基本數(shù)據(jù)類型的數(shù)組遂填,Kotlin提供了若干獨(dú)立的類,每一種基本數(shù)據(jù)類型都對(duì)應(yīng)一個(gè)澈蝙。例如:IntArray吓坚、ByteArray、CharArray灯荧、BooleanArray等礁击,所有這些類型都被編譯成普通的Java基本數(shù)據(jù)類型數(shù)組,比如int[]逗载、byte[]哆窿、char[]、boolean[]等厉斟,因此這些數(shù)組中的值存儲(chǔ)時(shí)并沒有裝箱挚躯,而是使用了可能的最高效的方式。
要?jiǎng)?chuàng)建一個(gè)基本數(shù)據(jù)類型的數(shù)組擦秽,有如下方法:
- 該類型的構(gòu)造方法接收size參數(shù)并返回一個(gè)使用對(duì)應(yīng)基本數(shù)據(jù)類型默認(rèn)值(通常是0)初始化好的數(shù)組码荔。
- 工廠函數(shù)(IntArray的intArrayOf,以及其他數(shù)組類型的函數(shù))接收變長(zhǎng)參數(shù)的值并創(chuàng)建存儲(chǔ)這些值的數(shù)組感挥。
- 另一種構(gòu)造方法缩搅,接收一個(gè)大小和一個(gè)用來初始化每個(gè)元素的lambda。
fun method() {
val arr = IntArray(3)
val arr2 = intArrayOf(0, 0, 0)
val arr3 = IntArray(3) { it + 10 }
for (i in arr.indices) {
Log.d("TAG", "zwm1, ${arr[i]}")
}
for (i in arr2.indices) {
Log.d("TAG", "zwm2, ${arr2[i]}")
}
for (i in arr3.indices) {
Log.d("TAG", "zwm3, ${arr3[i]}")
}
}
日志打哟ビ住:
2020-08-25 03:42:28.303 18958-18958/com.tomorrow.kotlindemo D/TAG: zwm1, 0
2020-08-25 03:42:28.304 18958-18958/com.tomorrow.kotlindemo D/TAG: zwm1, 0
2020-08-25 03:42:28.304 18958-18958/com.tomorrow.kotlindemo D/TAG: zwm2, 0
2020-08-25 03:42:28.304 18958-18958/com.tomorrow.kotlindemo D/TAG: zwm2, 0
2020-08-25 03:42:28.304 18958-18958/com.tomorrow.kotlindemo D/TAG: zwm3, 10
2020-08-25 03:42:28.305 18958-18958/com.tomorrow.kotlindemo D/TAG: zwm3, 11
2020-08-25 03:42:28.305 18958-18958/com.tomorrow.kotlindemo D/TAG: zwm3, 12
如果有一個(gè)持有基本數(shù)據(jù)類型裝箱后的數(shù)組或者集合硼瓣,可以用對(duì)應(yīng)的轉(zhuǎn)換函數(shù)把它們轉(zhuǎn)換成基本數(shù)據(jù)類型的數(shù)組,比如toIntArray置谦。
對(duì)于數(shù)組巨双,除了那些基本操作(獲取數(shù)組的長(zhǎng)度噪猾,獲取或者設(shè)置元素)外,Kotlin標(biāo)準(zhǔn)庫(kù)支持一套和集合相同的用于數(shù)組的擴(kuò)展函數(shù)筑累,如filter袱蜡、map等也適用于數(shù)組,包括基本數(shù)據(jù)類型的數(shù)組(注意慢宗,這些方法的返回值是列表而不是數(shù)組)坪蚁。
對(duì)數(shù)組使用forEachIndexed:
fun method() {
val arr = IntArray(3) { it + 10 }
arr.forEachIndexed { index, i -> Log.d("TAG", "zwm, $index:$i") }
}
日志打印:
2020-08-25 03:51:16.075 19707-19707/com.tomorrow.kotlindemo D/TAG: zwm, 0:10
2020-08-25 03:51:16.075 19707-19707/com.tomorrow.kotlindemo D/TAG: zwm, 1:11
2020-08-25 03:51:16.076 19707-19707/com.tomorrow.kotlindemo D/TAG: zwm, 2:12
七镜沽、運(yùn)算符重載及其他約定
1.重載算術(shù)運(yùn)算符
重載二元算術(shù)運(yùn)算符:
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point { //定義一個(gè)名為plus的方法
return Point(x + other.x, y + other.y)
}
}
fun method() {
val p1 = Point(200, 300)
val p2 = Point(300, 200)
val p = p1 + p2
Log.d("TAG", "zwm, x: ${p.x} y: ${p.y}")
}
日志打用粑睢:
2020-08-06 14:43:15.876 25513-25513/com.tomorrow.kotlindemo D/TAG: zwm, x: 500 y: 500
把運(yùn)算符定義為擴(kuò)展函數(shù):
data class Point(val x: Int, val y: Int)
operator fun Point.plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
fun method() {
val p1 = Point(200, 300)
val p2 = Point(300, 200)
val p = p1 + p2
Log.d("TAG", "zwm, x: ${p.x} y: ${p.y}")
}
日志打印:
2020-08-06 14:57:13.529 26210-26210/com.tomorrow.kotlindemo D/TAG: zwm, x: 500 y: 500
可重載的二元算術(shù)運(yùn)算符:
表達(dá)式 | 函數(shù)名 |
---|---|
a * b | times |
a / b | div |
a % b | mod |
a + b | plus |
a - b | minus |
定義一個(gè)運(yùn)算數(shù)類型不同的運(yùn)算符:
data class Point(val x: Int, val y: Int)
operator fun Point.times(scale: Double): Point {
return Point((x * scale).toInt(), (y * scale).toInt())
}
fun method() {
val p = Point(200, 300) * 1.5
Log.d("TAG", "zwm, x: ${p.x} y: ${p.y}")
}
日志打用遘浴:
2020-08-06 15:09:11.415 27811-27811/com.tomorrow.kotlindemo D/TAG: zwm, x: 300 y: 450
定義一個(gè)返回結(jié)果不同的運(yùn)算符:
data class Point(val x: Int, val y: Int)
operator fun Point.times(scale: Double): String {
return "${(x * scale).toInt()}, ${(y * scale).toInt()}"
}
fun method() {
val result = Point(200, 300) * 1.5
Log.d("TAG", "zwm, result: $result")
}
日志打幼炱ⅰ:
2020-08-06 15:14:35.870 28822-28822/com.tomorrow.kotlindemo D/TAG: zwm, result: 300, 450
Kotlin沒有為標(biāo)準(zhǔn)數(shù)字類型定義任何位運(yùn)算符,因此蔬墩,也不允許你為自定義類型定義它們译打。相反,它使用支持中綴調(diào)用語法的常規(guī)函數(shù)拇颅,可以為自定義類型定義相似的函數(shù)奏司。
Kotlin中用于執(zhí)行位運(yùn)算的函數(shù)列表:
- shl:帶符號(hào)左移
- shr:帶符號(hào)右移
- ushr:無符號(hào)右移
- and:按位與
- or:按位或
- xor:按位異或
- inv:按位取反
fun method() {
val result1 = 0x0F and 0xF0
val result2 = 0x0F or 0xF0
val result3 = 0x1 shl 4
Log.d("TAG", "zwm, $result1 $result2 $result3")
}
日志打印:
2020-08-06 15:28:50.149 29868-29868/com.tomorrow.kotlindemo D/TAG: zwm, 0 255 16
重載復(fù)合賦值運(yùn)算符:(plusAssign樟插、minusAssign韵洋、timesAssign等)
data class Point(var x: Int, var y: Int)
operator fun Point.plusAssign(other: Point) {
x += other.x
y += other.y
}
fun method() {
val p = Point(200, 300)
p.plusAssign(Point(300, 200))
Log.d("TAG", "zwm, $p")
}
日志打印:
2020-08-06 15:45:40.161 31500-31500/com.tomorrow.kotlindemo D/TAG: zwm, Point(x=500, y=500)
重載一元運(yùn)算符:
data class Point(val x: Int, val y: Int)
operator fun Point.unaryMinus(): Point {
return Point(-x, -y)
}
fun method() {
val p = Point(200, 300).unaryMinus()
Log.d("TAG", "zwm, $p")
}
日志打踊拼浮:
2020-08-06 15:51:35.020 31955-31955/com.tomorrow.kotlindemo D/TAG: zwm, Point(x=-200, y=-300)
可重載的一元算法的運(yùn)算符:
表達(dá)式 | 函數(shù)名 |
---|---|
+a | unaryPlus |
-a | unaryMinus |
!a | not |
++a搪缨,a++ | inc |
--a,a-- | dec |
2.重載比較運(yùn)算符
與算術(shù)運(yùn)算符一樣鸵熟,在Kotlin中副编,可以對(duì)任何對(duì)象使用比較運(yùn)算符(==、!=旅赢、>齿桃、<等)惑惶,而不僅僅限于基本數(shù)據(jù)類型煮盼。
等號(hào)運(yùn)算符:equals
如果在Kotlin中使用==運(yùn)算符,它將被轉(zhuǎn)換成equals方法的調(diào)用带污。使用!=運(yùn)算符也會(huì)被轉(zhuǎn)換成equals函數(shù)的調(diào)用僵控,明顯的差異在于,它們的結(jié)果是相反的鱼冀。注意报破,和所有其他運(yùn)算符不同的是悠就,==和!=可以用于可空運(yùn)算數(shù),因?yàn)檫@些運(yùn)算符事實(shí)上會(huì)檢查運(yùn)算數(shù)是否為null充易。比較a==b會(huì)檢查a是否為非空梗脾,如果是,就調(diào)用a.equals(b)盹靴,否則炸茧,只有兩個(gè)參數(shù)都是空引用,結(jié)果才是true稿静。
a == b -> a?.equals(b) ?: (b == null)
data class Point(val x: Int, val y: Int) {
override fun equals(other: Any?): Boolean {
if(other === this) return true //優(yōu)化:檢查參數(shù)是否與this是同一個(gè)對(duì)象
if(other !is Point) return false //檢查參數(shù)類型
return other.x == x && other.y == y //智能轉(zhuǎn)換為Point來訪問x梭冠、y屬性
}
}
fun method() {
val p1 = Point(200, 300)
val p2 = Point(200, 300)
val result = p1 == p2
val result2 = null == p1
Log.d("TAG", "zwm, result: $result result2: $result2")
}
日志打印:
2020-08-06 16:31:21.888 7153-7153/com.tomorrow.kotlindemo D/TAG: zwm, result: true result2: false
恒等運(yùn)算符(===)與Java中的==運(yùn)算符是完全相同的:檢查兩個(gè)參數(shù)是否是同一個(gè)對(duì)象的引用(如果是基本數(shù)據(jù)類型改备,檢查它們是否是相同的值)控漠。在實(shí)現(xiàn)了equals方法之后,通常會(huì)使用這個(gè)運(yùn)算符來優(yōu)化調(diào)用代碼悬钳。注意盐捷,===運(yùn)算符不能被重載。
equals函數(shù)之所以被標(biāo)記為override他去,那是因?yàn)榕c其他約定不同的是毙驯,這個(gè)方法的實(shí)現(xiàn)是在Any類中定義的(Kotlin中的所有對(duì)象都支持等式比較),Any中的基本方法就已經(jīng)標(biāo)記了operator灾测,并且函數(shù)的operator修飾符適用于所有實(shí)現(xiàn)或重寫它的方法爆价。
另外equals不能實(shí)現(xiàn)為擴(kuò)展函數(shù),因?yàn)槔^承自Any類的實(shí)現(xiàn)始終優(yōu)先于擴(kuò)展函數(shù)媳搪。
排序運(yùn)算符:compareTo
Kotlin中比較運(yùn)算符(<铭段、>、<=秦爆、>=)的使用將被轉(zhuǎn)換為compareTo序愚,compareTo的返回類型必須為Int。
a >= b -> a.compareTo(b) >= 0
data class Point(val x: Int, val y: Int): Comparable<Point> {
override fun compareTo(other: Point): Int {
return compareValuesBy(this, other, Point::x, Point::y) //按順序調(diào)用給定的方法等限,并比較它們的值
}
}
fun method() {
val p1 = Point(100, 200)
val p2 = Point(300, 100)
val p3 = Point(50, 500)
Log.d("TAG", "zwm, ${p1 > p2} ${p1 > p3}")
}
日志打影炙薄:
2020-08-06 16:50:28.966 10439-10439/com.tomorrow.kotlindemo D/TAG: zwm, false true
與equals一樣,operator修飾符已經(jīng)被用在了基類的接口中望门,因此在重寫該接口時(shí)無須再重復(fù)形娇。
3.集合與區(qū)間的約定
通過下標(biāo)來訪問元素:get和set
在Kotlin中,下標(biāo)運(yùn)算符是一個(gè)約定筹误,使用下標(biāo)運(yùn)算符獲取元素會(huì)被轉(zhuǎn)換為get運(yùn)算符方法的調(diào)用桐早,并且寫入元素將調(diào)用set。
實(shí)現(xiàn)get約定:
data class Point(val x: Int, val y: Int)
operator fun Point.get(index: Int): Int {
return when(index) {
0 -> x
1 -> y
else -> throw IndexOutOfBoundsException("Invalid coordinate $index")
}
}
fun method() {
val p = Point(100, 200)
Log.d("TAG", "zwm, ${p[0]} ${p[1]}")
}
日志打印:
2020-08-07 08:12:42.490 15636-15636/com.tomorrow.kotlindemo D/TAG: zwm, 100 200
注意哄酝,get的參數(shù)可以是任何類型友存,而不只是Int。例如陶衅,當(dāng)你對(duì)map使用下標(biāo)運(yùn)算符時(shí)屡立,參數(shù)類型是鍵的類型,它可以是任意類型搀军。還可以定義具有多個(gè)參數(shù)的get方法侠驯。如果需要使用不同的鍵類型訪問集合,也可以使用不同的參數(shù)類型定義多個(gè)重載的get方法奕巍。
實(shí)現(xiàn)set約定:
data class MutablePoint(var x: Int, var y: Int)
operator fun MutablePoint.set(index: Int, value: Int) {
return when(index) {
0 -> x = value
1 -> y = value
else -> throw IndexOutOfBoundsException("Invalid coordinate $index")
}
}
fun method() {
val p = MutablePoint(100, 200)
p[0] = 50
p[1] = 60
Log.d("TAG", "zwm, $p")
}
日志打右鞑摺:
2020-08-07 08:20:02.863 16265-16265/com.tomorrow.kotlindemo D/TAG: zwm, MutablePoint(x=50, y=60)
in的約定
in運(yùn)算符用于檢查某個(gè)對(duì)象是否屬于集合,相應(yīng)的函數(shù)叫作contains的止,in右邊的對(duì)象將會(huì)調(diào)用contains函數(shù)檩坚,in左邊的對(duì)象將會(huì)作為函數(shù)入?yún)ⅲ?/p>
data class Point(val x: Int, val y: Int)
data class Rectangle(val upperLeft: Point, val lowerRight: Point)
operator fun Rectangle.contains(p: Point): Boolean {
return p.x in upperLeft.x until lowerRight.x &&
p.y in upperLeft.y until lowerRight.y
}
fun method() {
val rect = Rectangle(Point(10, 20), Point(50, 50))
val p = Point(20, 30)
val result = p in rect
Log.d("TAG", "zwm, result: $result")
}
日志打印:
2020-08-07 08:33:33.966 17526-17526/? D/TAG: zwm, result: true
標(biāo)準(zhǔn)庫(kù)的until函數(shù)用于構(gòu)建一個(gè)開區(qū)間诅福,開區(qū)間是不包括最后一個(gè)點(diǎn)的區(qū)間匾委。例如,如果用10..20構(gòu)建一個(gè)普通的區(qū)間(閉區(qū)間)氓润,該區(qū)間則包括10到20的所有數(shù)字赂乐,包括20。開區(qū)間10 until 20包括10到19的數(shù)字咖气,但不包括20挨措。
rangeTo的約定
..運(yùn)算符是調(diào)用rangeTo函數(shù)的一個(gè)簡(jiǎn)潔方法。rangeTo函數(shù)返回一個(gè)區(qū)間崩溪,你可以為自己的類定義這個(gè)運(yùn)算符痹筛。但是挺尾,如果該類實(shí)現(xiàn)了Comparable接口荞彼,那么不需要了世杀;你可以通過Kotlin標(biāo)準(zhǔn)庫(kù)創(chuàng)建一個(gè)任意可比較元素的區(qū)間,這個(gè)庫(kù)定義了可以用于任何可比較元素的rangeTo函數(shù):
operator fun <T: Comparable<T>> T.rangeTo(that: T): ClosedRange<T>
data class Point(val x: Int, val y: Int): Comparable<Point> {
override fun compareTo(other: Point): Int {
return compareValuesBy(this, other, Point::x, Point::y) //按順序調(diào)用給定的方法乳幸,并比較它們的值
}
}
fun method() {
val p = Point(20, 30)..Point(200, 300)
val result = Point(100, 200) in p
Log.d("TAG", "zwm, result: $result")
}
日志打拥伤稀:
2020-08-07 08:50:12.484 17878-17878/com.tomorrow.kotlindemo D/TAG: zwm, result: true
rangeTo運(yùn)算符的優(yōu)先級(jí)低于算術(shù)運(yùn)算符,但是最好把參數(shù)括起來以免混淆:
fun method() {
val n = 9
Log.d("TAG", "zwm, ${5..(n+1)}")
}
日志打哟舛稀:
2020-08-07 08:59:06.502 18325-18325/? D/TAG: zwm, 5..10
使用forEach:
fun method() {
val n = 3
(0..n).forEach { Log.d("TAG", "zwm, $it") }
}
日志打臃贰:
2020-08-07 09:01:14.898 18611-18611/com.tomorrow.kotlindemo D/TAG: zwm, 0
2020-08-07 09:01:14.898 18611-18611/com.tomorrow.kotlindemo D/TAG: zwm, 1
2020-08-07 09:01:14.898 18611-18611/com.tomorrow.kotlindemo D/TAG: zwm, 2
2020-08-07 09:01:14.898 18611-18611/com.tomorrow.kotlindemo D/TAG: zwm, 3
在for循環(huán)中使用iterator的約定
在Kotlin中,for循環(huán)也可以使用in運(yùn)算符姿染,和做區(qū)間檢查一樣背亥。但是在這種情況下它的含義是不同的:它被用來執(zhí)行迭代。這意味著一個(gè)諸如for(x in list) {...} 將被轉(zhuǎn)換成list.iterator()的調(diào)用悬赏,然后就像在Java中一樣狡汉,在它上面重復(fù)調(diào)用hasNext和next方法:
fun method() {
val list = listOf(Point(10, 20), Point(20, 30))
for(item in list) {
Log.d("TAG", "zwm, $item")
}
}
日志打印:
2020-08-07 09:10:59.935 19483-19483/com.tomorrow.kotlindemo D/TAG: zwm, Point(x=10, y=20)
2020-08-07 09:10:59.935 19483-19483/com.tomorrow.kotlindemo D/TAG: zwm, Point(x=20, y=30)
注意闽颇,在Kotlin中盾戴,這也是一種約定,這意味著iterator方法可以被定義為擴(kuò)展函數(shù)兵多,這就解釋了為什么可以遍歷一個(gè)常規(guī)的Java字符串:標(biāo)準(zhǔn)庫(kù)已經(jīng)為CharSequence定義了一個(gè)擴(kuò)展函數(shù)iterator尖啡,而它是String的父類:
operator fun CharSequence.iterator(): CharIterator //這個(gè)庫(kù)函數(shù)讓迭代字符串成為可能
fun method() {
val str = "abc"
for(c in str) {
Log.d("TAG", "zwm, $c")
}
}
日志打印:
2020-08-07 09:16:30.770 19952-19952/? D/TAG: zwm, a
2020-08-07 09:16:30.770 19952-19952/? D/TAG: zwm, b
2020-08-07 09:16:30.771 19952-19952/? D/TAG: zwm, c
可以為自己的類定義iterator方法:
data class Point(val x: Int): Comparable<Point> {
override fun compareTo(other: Point): Int {
return compareValuesBy(this, other, Point::x)
}
}
operator fun ClosedRange<Point>.iterator(): Iterator<Point> =
object : Iterator<Point> {
var current = start
override fun hasNext() = current <= endInclusive
override fun next() = current.apply {
current = Point(this.x + 1)
}
}
fun method() {
val p = Point(5)..Point(8)
for(i in p) {
Log.d("TAG", "zwm, result: $i")
}
}
日志打邮1臁:
2020-08-07 09:42:07.980 21694-21694/? D/TAG: zwm, result: Point(x=5)
2020-08-07 09:42:07.980 21694-21694/? D/TAG: zwm, result: Point(x=6)
2020-08-07 09:42:07.980 21694-21694/? D/TAG: zwm, result: Point(x=7)
2020-08-07 09:42:07.980 21694-21694/? D/TAG: zwm, result: Point(x=8)
4.解構(gòu)聲明和組件函數(shù)
解構(gòu)聲明功能允許你展開單個(gè)復(fù)合值衅斩,并使用它來初始化多個(gè)單獨(dú)的變量:
data class Point(val x: Int, val y: Int)
fun method() {
val p = Point(100, 200)
val (x, y) = p
Log.d("TAG", "zwm, $x $y")
}
日志打印:
2020-08-07 09:53:25.024 23435-23435/com.tomorrow.kotlindemo D/TAG: zwm, 100 200
一個(gè)解構(gòu)聲明看起來像一個(gè)普通的變量聲明怠褐,但它在括號(hào)中有多個(gè)變量畏梆。事實(shí)上,解構(gòu)聲明再次用到了約定的原理奈懒。要在解構(gòu)聲明中初始化每個(gè)變量奠涌,將調(diào)用名為componentN的函數(shù),其中N是聲明中變量的位置磷杏。
對(duì)于數(shù)據(jù)類溜畅,編譯器為每個(gè)在主構(gòu)造方法中聲明的屬性生成一個(gè)componentN函數(shù)。而對(duì)于非數(shù)據(jù)類极祸,則需要手動(dòng)進(jìn)行聲明:
class Point(val x: Int, val y: Int) {
operator fun component1() = x
operator fun component2() = y
}
fun method() {
val p = Point(100, 200)
val (x, y) = p
Log.d("TAG", "zwm, $x $y")
}
日志打哟雀瘛:
2020-08-07 10:01:43.264 24067-24067/? D/TAG: zwm, 100 200
使用解構(gòu)聲明來返回多個(gè)值:
data class Point(val x: Int, val y: Int)
fun getPoint(arr: Array<Int>): Point {
return Point(arr[0], arr[1])
}
fun method() {
val arr = arrayOf(100, 200)
val (x, y) = getPoint(arr)
Log.d("TAG", "zwm, $x $y")
}
日志打印:
2020-08-07 10:13:35.270 25173-25173/com.tomorrow.kotlindemo D/TAG: zwm, 100 200
使用解構(gòu)聲明來處理集合:
data class Point(val x: Int, val y: Int)
fun getPoint(str: String): Point {
val (x, y) = str.split(',', limit = 2)
return Point(x.toInt(), y.toInt())
}
fun method() {
val arr = "100,200"
val (x, y) = getPoint(arr)
Log.d("TAG", "zwm, $x $y")
}
日志打右=稹:
2020-08-07 10:19:10.701 25706-25706/com.tomorrow.kotlindemo D/TAG: zwm, 100 200
用解構(gòu)聲明來遍歷map:
fun method() {
val map = mapOf( 1 to "one", 2 to "two", 3 to "three")
for((key, value) in map) {
Log.d("TAG", "zwm, $key $value")
}
}
日志打勇鸵:
2020-08-07 10:24:04.482 26149-26149/com.tomorrow.kotlindemo D/TAG: zwm, 1 one
2020-08-07 10:24:04.482 26149-26149/com.tomorrow.kotlindemo D/TAG: zwm, 2 two
2020-08-07 10:24:04.482 26149-26149/com.tomorrow.kotlindemo D/TAG: zwm, 3 three
5.重用屬性訪問的邏輯:委托屬性
委托屬性的基本操作
委托屬性的基本語法是這樣的:
class Foo {
var p: Type by Delegate()
}
屬性p將它的訪問器邏輯委托給了另一個(gè)對(duì)象:這里是Delegate類的一個(gè)新的實(shí)例。通過關(guān)鍵字by對(duì)其后的表達(dá)式求值來獲取這個(gè)對(duì)象汰规,關(guān)鍵字by可以用于任何符合屬性委托約定規(guī)則的對(duì)象汤功。編譯器生成的代碼如下:
class Foo {
private val delegate = Delegate() //編譯器會(huì)自動(dòng)生成一個(gè)輔助屬性
val p: Type
set(value: Type) = delegate.setValue(..., value)
get() = delegate.getValue(..)
}
例子:
class Foo {
var city : String by Delegate()
}
class Delegate {
operator fun getValue(foo: Foo, property: KProperty<*>): String {
Log.d("TAG", "zwm, Delegate getValue, property name: ${property.name}")
return "getValue"
}
operator fun setValue(foo: Foo, property: KProperty<*>, s: String) {
Log.d("TAG", "zwm, Delegate setValue, property name: ${property.name}, value: $s")
}
}
fun method() {
val foo = Foo()
Log.d("TAG", "zwm, foo.city 1: ${foo.city}")
Log.d("TAG", "zwm, foo.city 2: ${foo.city}")
foo.city = "guangzhou"
Log.d("TAG", "zwm, foo.city 3: ${foo.city}")
}
日志打印:
2020-08-07 14:14:53.351 8868-8868/com.tomorrow.kotlindemo D/TAG: zwm, Delegate getValue, property name: city
2020-08-07 14:14:53.351 8868-8868/com.tomorrow.kotlindemo D/TAG: zwm, foo.city 1: getValue
2020-08-07 14:14:53.351 8868-8868/com.tomorrow.kotlindemo D/TAG: zwm, Delegate getValue, property name: city
2020-08-07 14:14:53.351 8868-8868/com.tomorrow.kotlindemo D/TAG: zwm, foo.city 2: getValue
2020-08-07 14:14:53.352 8868-8868/com.tomorrow.kotlindemo D/TAG: zwm, Delegate setValue, property name: city, value: guangzhou
2020-08-07 14:14:53.352 8868-8868/com.tomorrow.kotlindemo D/TAG: zwm, Delegate getValue, property name: city
2020-08-07 14:14:53.352 8868-8868/com.tomorrow.kotlindemo D/TAG: zwm, foo.city 3: getValue
使用委托屬性:惰性初始化和by lazy()
惰性初始化是一種常見的模式溜哮,直到第一次訪問該屬性的時(shí)候滔金,才根據(jù)需要?jiǎng)?chuàng)建對(duì)象的一部分。當(dāng)初始化過程消耗大量資源并且在使用對(duì)象時(shí)并不總是需要數(shù)據(jù)時(shí)茂嗓,這個(gè)非常有用餐茵。使用委托屬性會(huì)讓代碼變得簡(jiǎn)單得多,可以封裝用于存儲(chǔ)值的支持屬性和確保該值只被初始化一次的邏輯述吸。在這里可以使用標(biāo)準(zhǔn)庫(kù)函數(shù)lazy返回的委托忿族,lazy函數(shù)返回一個(gè)對(duì)象锣笨,該對(duì)象具有一名為getValue且簽名正確的方法,因此可以把它與by關(guān)鍵字一起使用來創(chuàng)建一個(gè)委托屬性道批。lazy的參數(shù)是一個(gè)lambda错英,可以調(diào)用它來初始化這個(gè)值。
例子:
class Foo {
val city: String by lazy {
Log.d("TAG", "zwm, lazy")
"lazy"
}
}
fun method() {
val foo = Foo()
Log.d("TAG", "zwm, foo.city 1: ${foo.city}")
Log.d("TAG", "zwm, foo.city 2: ${foo.city}")
}
日志打勇”:
2020-08-07 14:22:50.565 9447-9447/com.tomorrow.kotlindemo D/TAG: zwm, lazy
2020-08-07 14:22:50.565 9447-9447/com.tomorrow.kotlindemo D/TAG: zwm, foo.city 1: lazy
2020-08-07 14:22:50.566 9447-9447/com.tomorrow.kotlindemo D/TAG: zwm, foo.city 2: lazy
by lazy語法的特點(diǎn)如下:
- 該變量必須是引用不可變的椭岩,而不能通過var來聲明。
- 在被首次調(diào)用時(shí)璃赡,才會(huì)進(jìn)行賦值操作判哥。一旦被賦值,后續(xù)它將不能被更改碉考。
lazy的背后是接受一個(gè)lambda并返回一個(gè)Lazy<T>實(shí)例的函數(shù)塌计,第一次訪問該屬性時(shí),會(huì)執(zhí)行l(wèi)azy對(duì)應(yīng)的Lambda表達(dá)式并記錄結(jié)果侯谁,后續(xù)訪問該屬性時(shí)只是返回記錄的結(jié)果夺荒。另外系統(tǒng)會(huì)給lazy屬性默認(rèn)加上同步鎖,也就是LazyThreadSafetyMode.SYNCHRONIZED良蒸,它在同一時(shí)刻只允許一個(gè)線程對(duì)lazy屬性進(jìn)行初始化技扼,所以它是線程安全的。但若你能確認(rèn)該屬性可以并行執(zhí)行嫩痰,沒有線程安全問題剿吻,那么可以給lazy傳遞LazyThreadSafetyMode.PUBLICATION參數(shù)。你還可以給lazy傳遞LazyThreadSafetyMode.NONE參數(shù)串纺,這將不會(huì)有任何線程方面的開銷丽旅,當(dāng)然也不會(huì)有任何線程安全的保證。比如:
class Person(val color: String) {
val sex: String by lazy(LazyThreadSafetyMode.PUBLICATION) {
//并行模式
if(color == "yellow") "male" else "female"
}
}
class Person(val color: String) {
val sex: String by lazy(LazyThreadSafetyMode.NONE) {
//不做任何線程保證也不會(huì)有任何線程開銷
if(color == "yellow") "male" else "female"
}
}
Delegates.observable函數(shù)可以用來添加屬性更改的觀察者:
class Foo {
var city: String by Delegates.observable("init") {
prop, old, new ->
Log.d("TAG", "zwm, observable: ${prop.name} $old $new")
}
}
fun method() {
val foo = Foo()
Log.d("TAG", "zwm, foo.city: ${foo.city}")
foo.city = "guangzhou"
}
日志打臃墓住:
2020-08-07 14:28:44.590 10176-10176/com.tomorrow.kotlindemo D/TAG: zwm, foo.city: init
2020-08-07 14:28:44.591 10176-10176/com.tomorrow.kotlindemo D/TAG: zwm, observable: city init guangzhou
委托屬性可以使用任意map來作為屬性委托榄笙,來靈活處理具有可變屬性集的對(duì)象:
class Site(val map: Map<String, Any?>) {
val name: String by map
val city: String by map
}
fun method() {
val map = mutableMapOf("name" to "Tomy", "city" to "gz")
val site = Site(map)
Log.d("TAG", "zwm, name: ${site.name} url: ${site.city}")
map["name"] = "Zhang"
Log.d("TAG", "zwm, name: ${site.name} url: ${site.city}")
}
日志打印:
2020-08-07 14:49:31.047 11820-11820/com.tomorrow.kotlindemo D/TAG: zwm, name: Tomy url: gz
2020-08-07 14:49:31.047 11820-11820/com.tomorrow.kotlindemo D/TAG: zwm, name: Zhang url: gz
八祷蝌、高階函數(shù):Lambda作為形參和返回值
1.聲明高階函數(shù)
高階函數(shù)就是以另一個(gè)函數(shù)作為參數(shù)或者返回值的函數(shù)茅撞。在Kotlin中,函數(shù)可以用lambda或者函數(shù)引用來表示巨朦。
Kotlin中的函數(shù)類型語法:
(int, String) -> Unit //格式:(參數(shù)類型) -> 返回類型米丘,如果沒有參數(shù)類型就用()來表示
例如:
val sum = { x: Int, y:Int -> x + y }
val sum: (Int, Int) -> Int = { x, y -> x + y }
調(diào)用作為參數(shù)的函數(shù):
fun twoAndThree(operation: (Int, Int) -> Int) { //定義一個(gè)函數(shù)類型的參數(shù)
val result = operation(2, 3)
Log.d("TAG", "zwm, result: $result")
}
fun method() {
twoAndThree { x, y -> x + y }
}
日志打印:
2020-08-12 08:55:52.236 21495-21495/com.tomorrow.kotlindemo D/TAG: zwm, result: 5
在Java中可以很簡(jiǎn)單地調(diào)用使用了函數(shù)類型的Kotlin函數(shù)糊啡。Java 8的lambda會(huì)被自動(dòng)轉(zhuǎn)換為函數(shù)類型的值:
//shapes.kt
fun twoAndThree(operation: (Int, Int) -> Int): Int {
val result = operation(2, 3)
Log.d("TAG", "zwm, result: $result")
return result
}
//JavaDemo.java
int result = ShapesKt.twoAndThree(x, y -> x + y);
在舊版的Java中(Java 8以前)拄查,可以傳遞一個(gè)實(shí)現(xiàn)了函數(shù)接口中的invoke方法的匿名類的實(shí)例:
//shapes.kt
package com.tomorrow.kotlindemo.example
import android.util.Log
fun twoAndThree(operation: (Int, Int) -> Int): Int { //定義一個(gè)函數(shù)類型的參數(shù)
val result = operation(2, 3)
Log.d("TAG", "zwm, result: $result")
return result
}
//JavaDemo.java
package com.tomorrow.kotlindemo;
import android.util.Log;
import com.tomorrow.kotlindemo.example.ShapesKt;
import kotlin.jvm.functions.Function2;
public class JavaDemo {
public void testDemo() {
int result = ShapesKt.twoAndThree(new Function2<Integer, Integer, Integer>() {
@Override
public Integer invoke(Integer integer, Integer integer2) {
return integer + integer2;
}
});
Log.d("TAG", "zwm, java result: " + result);
}
}
日志打印:
2020-08-12 09:22:31.998 24355-24355/com.tomorrow.kotlindemo D/TAG: zwm, result: 5
2020-08-12 09:22:31.998 24355-24355/com.tomorrow.kotlindemo D/TAG: zwm, java result: 5
函數(shù)類型的參數(shù)默認(rèn)值和null值:
fun twoAndThree(operation: (Int, Int) -> Int = { x, y -> x * y }): Int { //定義一個(gè)函數(shù)類型的參數(shù)
val result = operation(2, 3)
Log.d("TAG", "zwm, result: $result")
return result
}
fun method() {
twoAndThree()
}
日志打优镄睢:
2020-08-12 09:28:53.663 25496-25496/com.tomorrow.kotlindemo D/TAG: zwm, result: 6
fun twoAndThree(operation: ((Int, Int) -> Int)? = null): Int {
if(operation != null) { //顯式判空
val result = operation(2, 3)
Log.d("TAG", "zwm, result: $result")
return result
}
return -1
}
fun method() {
twoAndThree()
}
fun twoAndThree(operation: ((Int, Int) -> Int)? = null): Int {
val result = operation?.invoke(2, 3) ?: (2 + 3) //函數(shù)類型是一個(gè)包含invoke方法的接口的具體實(shí)現(xiàn)
Log.d("TAG", "zwm, result: $result")
return result
}
fun method() {
twoAndThree()
}
日志打佣榉觥:
2020-08-12 09:35:00.001 25823-25823/com.tomorrow.kotlindemo D/TAG: zwm, result: 5
返回函數(shù)的函數(shù):
fun getAddCalculator(): (Int, Int) -> Int {
return { x, y -> x + y }
}
fun method() {
val calculator = getAddCalculator()
val result = calculator(2, 3)
Log.d("TAG", "zwm, result: $result")
}
日志打影唷:
2020-08-12 09:45:19.782 26722-26722/com.tomorrow.kotlindemo D/TAG: zwm, result: 5
2.內(nèi)聯(lián)函數(shù):消除lambda帶來的運(yùn)行時(shí)開銷
Lambda表達(dá)式會(huì)被正常地編譯成匿名類,這表示每調(diào)用一次lambda表達(dá)式稍算,一個(gè)額外的類就會(huì)被創(chuàng)建典尾。并且如果lambda捕捉了某個(gè)變量,那么每次調(diào)用的時(shí)候都會(huì)創(chuàng)建一個(gè)新的對(duì)象邪蛔。這會(huì)帶來運(yùn)行時(shí)的額外開銷,導(dǎo)致使用lambda比使用一個(gè)直接執(zhí)行相同代碼的函數(shù)效率更低扎狱。
如果使用inline修飾符標(biāo)記一個(gè)函數(shù)侧到,在函數(shù)被使用的時(shí)候編譯器并不會(huì)生成函數(shù)調(diào)用的代碼,而是使用函數(shù)實(shí)現(xiàn)的真實(shí)代碼替換每一次的函數(shù)調(diào)用:
inline fun twoAndThree(operator: (Int, Int) -> Int) { //內(nèi)聯(lián)函數(shù)定義
val result = operator(2, 3)
Log.d("TAG", "zwm, result: $result")
}
fun method() {
Log.d("TAG", "zwm, before inline")
twoAndThree{ x, y -> x + y } //內(nèi)聯(lián)函數(shù)調(diào)用
Log.d("TAG", "zwm, after inline")
}
不是所有使用lambda的函數(shù)都可以被內(nèi)聯(lián)淤击。當(dāng)函數(shù)被內(nèi)聯(lián)的時(shí)候匠抗,作為參數(shù)的lambda表達(dá)式的函數(shù)體會(huì)被直接替換到最終生成的代碼中。這將限制函數(shù)體中的對(duì)應(yīng)lambda參數(shù)的使用污抬。如果lambda參數(shù)被調(diào)用汞贸,這樣的代碼能被容易地內(nèi)聯(lián)。但如果lambda參數(shù)在某個(gè)地方被保存起來印机,以便后面可以繼續(xù)使用矢腻,lambda表達(dá)式的代碼將不能被內(nèi)聯(lián),因?yàn)楸仨氁幸粋€(gè)包含這些代碼的對(duì)象存在:
inline fun twoAndThree(operator: (Int, Int) -> Int) { //內(nèi)聯(lián)函數(shù)定義
val result = operator(2, 3)
Log.d("TAG", "zwm, result: $result")
}
fun callInlineMethod(param: (Int, Int) -> Int) {
Log.d("TAG", "zwm, before call inline")
twoAndThree(param) //在調(diào)用的地方還沒有l(wèi)ambda射赛,因此沒有被內(nèi)聯(lián)
Log.d("TAG", "zwm, after call inline")
}
如果一個(gè)函數(shù)期望兩個(gè)或更多l(xiāng)ambda參數(shù)多柑,可以選擇只內(nèi)聯(lián)其中一些參數(shù)。因?yàn)橐粋€(gè)lambda可能會(huì)包含很多代碼或者以不允許內(nèi)聯(lián)的方式使用:
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
//...
}
注意楣责,編譯器完全支持內(nèi)聯(lián)跨模塊的函數(shù)或者第三方庫(kù)定義的函數(shù)竣灌。也可以在Java中調(diào)用絕大部分內(nèi)聯(lián)函數(shù),但這些調(diào)用并不會(huì)被內(nèi)聯(lián)秆麸,而是編譯成普通的函數(shù)調(diào)用初嘹。
內(nèi)聯(lián)集合操作:
filter和map函數(shù)都被聲明為inline函數(shù),所以它們的函數(shù)體會(huì)被內(nèi)聯(lián)沮趣,因此不會(huì)產(chǎn)生額外的類或者對(duì)象屯烦。如果有大量元素需要處理,中間集合的運(yùn)行開銷將成為不可忽視的問題房铭,這時(shí)可以在調(diào)用鏈后加一個(gè)asSequence調(diào)用漫贞,用序列來替代集合。但是用來處理序列的lambda沒有被內(nèi)聯(lián)育叁。每一個(gè)中間序列被表示成把lambda保存在其字段中的對(duì)象迅脐,而末端操作會(huì)導(dǎo)致由每一個(gè)中間序列調(diào)用組成的調(diào)用鏈被執(zhí)行。因此豪嗽,即便序列上的操作是惰性的谴蔑,你不應(yīng)該總是試圖在集合操作的調(diào)用鏈后加上asSequence豌骏。這只在處理大量數(shù)據(jù)的集合時(shí)有用,小的集合可以用普通的集合操作處理隐锭。
決定何時(shí)將函數(shù)聲明成內(nèi)聯(lián):
使用inline關(guān)鍵字只能提高帶有l(wèi)ambda參數(shù)的函數(shù)的性能窃躲,其他的情況需要額外的度量和研究。對(duì)于普通的函數(shù)調(diào)用钦睡,JVM已經(jīng)提供了強(qiáng)大的內(nèi)聯(lián)支持蒂窒。
Kotlin庫(kù)中withLock函數(shù)的定義:
fun <T> Lock.withLock(action: () -> T): T {
lock()
try {
return action()
} finally {
unlock()
}
}
Kotlin的withLock語句的使用:
fun method() {
val lock: Lock = ReentrantLock()
lock.withLock { Log.d("TAG", "zwm, with lock") }
}
日志打印:
2020-08-12 13:32:21.455 9622-9622/com.tomorrow.kotlindemo D/TAG: zwm, with lock
Java 7的try-with-resource語句的使用:
public String testDemo(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
Kotlin標(biāo)準(zhǔn)庫(kù)中有個(gè)use函數(shù)荞怒,是一個(gè)擴(kuò)展函數(shù)洒琢,被用來操作可關(guān)閉的資源,它接收一個(gè)lambda作為參數(shù)褐桌。這個(gè)方法調(diào)用lambda并且確保資源被關(guān)閉衰抑,無論lambda正常執(zhí)行還是拋出了異常。當(dāng)然荧嵌,use函數(shù)是內(nèi)聯(lián)函數(shù)呛踊,所以使用它并不會(huì)引發(fā)任何性能開銷:
fun method(path: String): String {
BufferedReader(FileReader(path)).use { br -> return br.readLine() }
}
3.高階函數(shù)中的控制流
lambda中的返回語句:從一個(gè)封閉的函數(shù)返回
如果你在lambda中使用return關(guān)鍵字,它會(huì)從調(diào)用lambda的函數(shù)中返回啦撮,并不只是從lambda中返回谭网。這樣的return語句叫作非局部返回,因?yàn)樗鼜囊粋€(gè)比包含return的代碼塊更大的代碼塊中返回了赃春。
需要注意的是蜻底,只有在以lambda作為參數(shù)的函數(shù)是內(nèi)聯(lián)函數(shù)的時(shí)候才能從更外層的函數(shù)返回。forEach的函數(shù)體和lambda的函數(shù)體一起被內(nèi)聯(lián)了聘鳞,所以在編譯的時(shí)候能很容易做到從包含它的函數(shù)中返回薄辅。在一個(gè)非內(nèi)聯(lián)函數(shù)的lambda中使用return表達(dá)式是不允許的。
fun method() {
val list = listOf(Person("Kotlin"), Person("Java"))
list.forEach {
Log.d("TAG", "zwm, name: ${it.name}")
return
}
Log.d("TAG", "zwm, method end")
}
日志打涌倭А:
2020-08-12 14:04:43.195 14730-14730/com.tomorrow.kotlindemo D/TAG: zwm, name: Kotlin
從lambda返回:使用標(biāo)簽返回
想從一個(gè)lambda表達(dá)式處返回站楚, 你可以標(biāo)記它,然后在return關(guān)鍵字后面引用這標(biāo)簽搏嗡。要標(biāo)記一個(gè)lambda表達(dá)式窿春,在lambda的花括號(hào)之前放一個(gè)標(biāo)簽名(可以是任何標(biāo)識(shí)符),接著放一個(gè)@符號(hào)采盒。要從一個(gè)lambda返回旧乞,在return關(guān)鍵字后放一個(gè)@符號(hào),接著放標(biāo)簽名磅氨。
fun method() {
val list = listOf(Person("Kotlin"), Person("Java"))
list.forEach label@{
Log.d("TAG", "zwm, name: ${it.name}")
return@label
}
Log.d("TAG", "zwm, method end")
}
日志打映咂堋:
2020-08-12 14:26:21.882 16310-16310/? D/TAG: zwm, name: Kotlin
2020-08-12 14:26:21.883 16310-16310/? D/TAG: zwm, name: Java
2020-08-12 14:26:21.883 16310-16310/? D/TAG: zwm, method end
使用lambda作為參數(shù)的函數(shù)的函數(shù)名可以作為標(biāo)簽:
fun method() {
val list = listOf(Person("Kotlin"), Person("Java"))
list.forEach {
Log.d("TAG", "zwm, name: ${it.name}")
return@forEach
}
Log.d("TAG", "zwm, method end")
}
日志打印:
2020-08-12 14:28:12.182 16512-16512/com.tomorrow.kotlindemo D/TAG: zwm, name: Kotlin
2020-08-12 14:28:12.182 16512-16512/com.tomorrow.kotlindemo D/TAG: zwm, name: Java
2020-08-12 14:28:12.183 16512-16512/com.tomorrow.kotlindemo D/TAG: zwm, method end
如果你顯式地指定了lambda表達(dá)式的標(biāo)簽烦租,再使用函數(shù)名作為標(biāo)簽沒有任何效果延赌,一個(gè)lambda表達(dá)式的標(biāo)簽數(shù)量不能多于一個(gè)除盏。
帶標(biāo)簽的this表達(dá)式
如果你給帶接收者的lambda指定標(biāo)簽,就可以通過對(duì)應(yīng)的帶有標(biāo)簽的this表達(dá)式訪問它的隱式接收者:
fun method() {
val result = StringBuilder().apply sb@{
listOf(1, 2, 3).apply {
this@sb.append(this.toString())
}
}
Log.d("TAG", "zwm, result: ${result.toString()}")
}
日志打哟煲浴:
2020-08-12 14:44:00.654 17336-17336/com.tomorrow.kotlindemo D/TAG: zwm, result: [1, 2, 3]
匿名函數(shù):默認(rèn)使用局部返回
在匿名函數(shù)中使用return:
data class Person(val name: String)
fun method() {
val list = listOf(Person("Kotlin"), Person("Java"))
list.forEach(fun (person) {
Log.d("TAG", "zwm, name: ${person.name}")
return
})
Log.d("TAG", "zwm, method end")
}
日志打诱呷洹:
2020-08-12 14:53:45.399 18383-18383/? D/TAG: zwm, name: Kotlin
2020-08-12 14:53:45.399 18383-18383/? D/TAG: zwm, name: Java
2020-08-12 14:53:45.399 18383-18383/? D/TAG: zwm, method end
在filter中使用匿名函數(shù):
data class Person(val name: String)
fun method() {
val list = listOf(Person("Kotlin"), Person("Java"))
val result = list.filter(fun (person): Boolean {
return person.name == "Kotlin"
})
Log.d("TAG", "zwm, method end: $result")
}
日志打印:
2020-08-12 14:56:31.894 18622-18622/? D/TAG: zwm, method end: [Person(name=Kotlin)]
使用表達(dá)式體匿名函數(shù):
data class Person(val name: String)
fun method() {
val list = listOf(Person("Kotlin"), Person("Java"))
val result = list.filter(fun (person) = person.name == "Kotlin" )
Log.d("TAG", "zwm, method end: $result")
}
日志打悠伞:
2020-08-12 14:59:32.693 19035-19035/com.tomorrow.kotlindemo D/TAG: zwm, method end: [Person(name=Kotlin)]
在匿名函數(shù)中踱侣,不帶標(biāo)簽的return表達(dá)式會(huì)從匿名函數(shù)返回,而不是從包含匿名函數(shù)的函數(shù)返回大磺。這條規(guī)則很簡(jiǎn)單:return從最近的使用fun關(guān)鍵字聲明的函數(shù)返回抡句。lambda表達(dá)式?jīng)]有使用fun關(guān)鍵字,所以lambda中的return從最外層的函數(shù)返回量没。匿名函數(shù)使用了fun玉转,因此return表達(dá)式從匿名函數(shù)返回突想,而不是從最外層的函數(shù)返回殴蹄。盡管匿名函數(shù)看起來跟普通函數(shù)很相似,但它其實(shí)是lambda表達(dá)式的另一種語法形式而已猾担。