Kotlin學(xué)習(xí)筆記:概述
Kotlin學(xué)習(xí)筆記:基本語(yǔ)法和函數(shù)
Kotlin學(xué)習(xí)筆記:類和接口
Kotlin學(xué)習(xí)筆記:lambda編程
Kotlin學(xué)習(xí)筆記:類型系統(tǒng)
Kotlin學(xué)習(xí)筆記:泛型
Kotlin學(xué)習(xí)筆記:注解和反射
什么是lambda表達(dá)式
“Lambda 表達(dá)式”(lambda expression)是一個(gè)匿名函數(shù)曙蒸,Lambda表達(dá)式基于數(shù)學(xué)中的λ演算得名,直接對(duì)應(yīng)于其中的lambda抽象(lambda abstraction)刺彩,是一個(gè)匿名函數(shù)说莫,即沒(méi)有函數(shù)名的函數(shù)屁柏。
lambda表達(dá)式本質(zhì)上是可以傳遞給其他函數(shù)的一段代碼滴铅。lambda可以作為函數(shù)的參數(shù)和返回值瓮钥。大大簡(jiǎn)化了代碼底桂。
lambda表達(dá)式的語(yǔ)法為
集合的函數(shù)式API
- filter
filter 可以移除集合中不滿足條件的元素式散。
>>> val list = listOf(1, 2, 3, 4)
>>> list.filter { it % 2 == 0 }
[2, 4]
- map
map函數(shù)對(duì)集合中的每個(gè)元素應(yīng)用給定的函數(shù)并把結(jié)果收集到一個(gè)集合中筋遭。
>>> val list = listOf(1, 2, 3, 4)
>>> list.map { it * it }
[1, 4, 9, 16]
- all,any暴拄,find漓滔,count
這些主要用于對(duì)集合的判定
假如我們需要判定一群人中的年齡是否小于28歲
val canBeInClub27 = { p: Person -> p.age <= 27 }
使用all判定是否所有人都小于28歲
>>> val people = listOf(Person("Alice", 27), Person("Bob", 31))
>>> println(people.all(canBeInClub27))
false
使用any判定其中的一些人小于28歲
>>> println(people.any(canBeInClub27))
true
使用count計(jì)算小于28歲的人的個(gè)數(shù)
>>> val people = listOf(Person("Alice", 27), Person("Bob", 31))
>>> println(people.count(canBeInClub27))
1
使用find找到所有小于28歲的人信息
>>> val people = listOf(Person("Alice", 27), Person("Bob", 31))
>>> println(people.find(canBeInClub27))
Person(name=Alice, age=27)
- groupBy
groupBy可以用于將列表轉(zhuǎn)換成分組的map
>>> val people = listOf(Person("Alice", 31),
... Person("Bob", 29), Person("Carol", 31))
>>> println(people.groupBy { it.age })
- flatmap和flatten
flatMap函數(shù)做兩件事情:首先根據(jù)作為實(shí)參給定的函數(shù)對(duì)集合中的每個(gè)元素做變換(maps),然后把多個(gè)列表合并(flatten)成一個(gè)列表乖篷。
>>> val strings = listOf("abc", "def")
>>> println(strings.flatMap { it.toList() })
[a, b, c, d, e, f]
flatten可以將多個(gè)集合的元素合并到一個(gè)集合中响驴。
>>> val langs = listOf(listOf("js"), listOf("Java", "Kotlin"), listOf("C++", "PHP", "Python"))
>>> println(langs.flatten())
[js, Java, Kotlin, C++, PHP, Python]
序列
前面講述了很多鏈?zhǔn)郊险{(diào)用的例子,但是這些函數(shù)會(huì)及早的創(chuàng)建中間集合撕蔼,也就是說(shuō)每一步的中間結(jié)果都存儲(chǔ)在一個(gè)臨時(shí)列表里豁鲤。所以,當(dāng)直接使用鏈?zhǔn)郊系暮瘮?shù)處理集合元素?cái)?shù)量特別多的情況下鲸沮,性能比較低琳骡。而序列可以避免創(chuàng)建臨時(shí)中間變量。
舉例來(lái)講
有一個(gè)Person類讼溺,它有名字和年齡兩個(gè)屬性楣号。
data class Person(val name: String, val age:Int)
要找出名字前綴是“J”的人的姓名
用鏈?zhǔn)郊虾瘮?shù)
persons.map { person -> person.name }
.filter { it.startsWith("J") }
.toList()
編譯代碼,再將其反編譯
//....
Object element$iv$iv;
while(var4.hasNext()) {
element$iv$iv = var4.next();
Person person = (Person)element$iv$iv;
String var11 = person.getName();
destination$iv$iv.add(var11);
}
//...
用序列實(shí)現(xiàn)
persons.asSequence()
.map { person -> person.name }
.filter { it.startsWith("J") }
.toList()
反編譯后代碼
List persons = CollectionsKt.listOf(new Person[]{new Person("Jack", 25), new Person("Cat", 29)});
SequencesKt.toList(SequencesKt.filter(SequencesKt.map(CollectionsKt.asSequence((Iterable)persons), (Function1)null.INSTANCE), (Function1)null.INSTANCE));
使用Java函數(shù)式接口
首先說(shuō)函數(shù)式接口的定義:只有一個(gè)抽象方法的接口稱為函數(shù)式接口怒坯,也就是SAM(單抽象方法)炫狱。
在Android中,最常用的接口就是OnClickListener了【炊牵現(xiàn)在我們以監(jiān)聽一個(gè)button的點(diǎn)擊事件為例毕荐,來(lái)說(shuō)明kotlin是如何使用Java函數(shù)式接口的。
用java編寫監(jiān)聽button點(diǎn)擊事件
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {...}
});
用kotlin編寫button點(diǎn)擊事件
btn.setOnClickListener{...}
我們還記得在函數(shù)文章中艳馒,我們用過(guò)匿名表達(dá)式憎亚。
val vto = layout.viewTreeObserver
vto.addOnGlobalLayoutListener(object: ViewTreeObserver.OnGlobalLayoutListener {
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
override fun onGlobalLayout(){
layout.viewTreeObserver.removeOnGlobalLayoutListener(this)
val height = layout.measuredHeight
val width = layout.measuredWidth
}
})
OnGlobalLayoutListener接口也只有一個(gè)方法,符合函數(shù)式接口的定義弄慰。那么第美,這個(gè)方法可以用lambda簡(jiǎn)化嗎?答案是不可以陆爽。因?yàn)榘?code>layout.viewTreeObserver.removeOnGlobalLayoutListener(this)語(yǔ)句什往。
在lambda 內(nèi)部沒(méi)有匿名對(duì)象那樣的this。這意味著沒(méi)有辦法引用到lambda轉(zhuǎn)換成的匿名類實(shí)例慌闭。
帶接收者的lambda:with和apply
很多語(yǔ)言都有這樣的語(yǔ)句:可以用他們對(duì)同一個(gè)對(duì)象執(zhí)行多次操作别威,而不需要把對(duì)象的名稱寫出來(lái)躯舔。
with 和apply就是這樣的語(yǔ)句搂橙。
- with
還是以一個(gè)例子開始:創(chuàng)建一個(gè)函數(shù)打印字母表髓迎。
一般情況下,都這樣寫
fun alphabet(): String {
val result = StringBuilder()
for (letter in 'A'..'Z') {
result.append(letter)
}
result.append("\nNow I know the alphabet!")
return result.toString()
}
>>> println(alphabet())
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Now I know the alphabet!
而用with叛买,可以這樣寫
fun alphabet(): String {
val result = StringBuilder()
return with(result)
{ for (letter in 'A'..'Z')
{this.append(letter)}
append("\nNow I know the alphabet!")
this.toString()
}
}
- apply
apply幾乎和with一模一樣豺妓,唯一的區(qū)別是apply始終會(huì)返回作為實(shí)參傳遞給他的對(duì)象惜互。
fun alphabet() = StringBuilder().apply{
for (letter in 'A'..'Z') {
append(letter)
}
append("\nNow I know the alphabet!")
}.toString()
高階函數(shù)
高階函數(shù):以另一個(gè)函數(shù)作為參數(shù)或者返回值的函數(shù)。
- 函數(shù)類型
為了聲明一個(gè)以lambda作為實(shí)參的函數(shù)琳拭,需要知道如何聲明對(duì)應(yīng)的形參類型训堆。
語(yǔ)法:將函數(shù)參數(shù)類型放在括號(hào)中,緊接著是一個(gè)箭頭和函數(shù)的返回類型
- 調(diào)用作為參數(shù)的函數(shù)類型
fun searchRepos(
service: ZhihuService,
query:String,
onSuccess:(repos: List<Repo>) -> Unit,
onError: (error: String) -> Unit) {
//...
}