Kotlin 知識(shí)梳理系列文章
Kotlin 知識(shí)梳理(1) - Kotlin 基礎(chǔ)
Kotlin 知識(shí)梳理(2) - 函數(shù)的定義與調(diào)用
Kotlin 知識(shí)梳理(3) - 類豆茫、對(duì)象和接口
Kotlin 知識(shí)梳理(4) - 數(shù)據(jù)類注竿、類委托 及 object 關(guān)鍵字
Kotlin 知識(shí)梳理(5) - lambda 表達(dá)式和成員引用
Kotlin 知識(shí)梳理(6) - Kotlin 的可空性
Kotlin 知識(shí)梳理(7) - Kotlin 的類型系統(tǒng)
Kotlin 知識(shí)梳理(8) - 運(yùn)算符重載及其他約定
Kotlin 知識(shí)梳理(9) - 委托屬性
Kotlin 知識(shí)梳理(10) - 高階函數(shù):Lambda 作為形參或返回值
Kotlin 知識(shí)梳理(11) - 內(nèi)聯(lián)函數(shù)
Kotlin 知識(shí)梳理(12) - 泛型類型參數(shù)
一、本文概要
本文是對(duì)<<Kotlin in Action>>
的學(xué)習(xí)筆記粉怕,如果需要運(yùn)行相應(yīng)的代碼可以訪問(wèn)在線環(huán)境 try.kotlinlang.org,這部分的思維導(dǎo)圖為:
二袱蜡、在 kotlin 中創(chuàng)建集合
在kotlin
中,創(chuàng)建HashSet
秤掌、ArrayList
和HashMap
的方法如下:
通過(guò)打印這些集合的類型,可以看到是采用的標(biāo)準(zhǔn)的
Java
集合類:這么做的原因鹰霍,是因?yàn)槭褂脴?biāo)準(zhǔn)的
Java
集合使kotlin
可以更容易地與Java
代碼交互闻鉴。當(dāng)從Kotlin
調(diào)用Java
函數(shù)的時(shí)候,不用轉(zhuǎn)換它的集合類來(lái)匹配Java
的類茂洒,反之亦然孟岛。
在這些集合對(duì)象上,我們除了可以使用Java
當(dāng)中定義的基本函數(shù)以外督勺,還可以使用kotlin
提供的擴(kuò)展方法渠羞,例如下面的last
和max
:
運(yùn)行結(jié)果為:
三、讓函數(shù)更好調(diào)用
下面智哀,我們定義一個(gè)函數(shù)次询,它的作用的是打印集合當(dāng)中的元素,并指定元素之間添加分隔符瓷叫、前綴和后綴:
運(yùn)行結(jié)果為:
3.1 命名參數(shù)
在上面的例子中屯吊,我們指定了分隔符、前綴和后綴三個(gè)參數(shù)摹菠,但是對(duì)于這個(gè)函數(shù)的使用者來(lái)說(shuō)雌芽,如果不去看這些函數(shù)的聲明,很難看出這些String
類型的含義辨嗽,這時(shí)候就可以使用 命名參數(shù) 的方法來(lái)調(diào)用,這有兩點(diǎn)好處:
- 增加函數(shù)的可讀性
- 以想要的順序指定需要的參數(shù)
下面淮腾,我們使用命名參數(shù)糟需,并在不改變函數(shù)定義的情況下,改變傳入?yún)?shù)的順序谷朝,也可以得到和上面相同的運(yùn)行結(jié)果:
對(duì)于命名參數(shù)洲押,有以下幾點(diǎn)需要注意:
- 如果在調(diào)用一個(gè)函數(shù)時(shí),指明了一個(gè)參數(shù)的名稱圆凰,那它之后的所有參數(shù)都要表明名稱杈帐。
- 當(dāng)調(diào)用
Java
函數(shù)時(shí),不能使用命名參數(shù)专钉。
3.2 默認(rèn)參數(shù)值
在kotlin
中挑童,可以在聲明函數(shù)的時(shí)候,指定參數(shù)的默認(rèn)值跃须,這樣就可以避免創(chuàng)建重載的函數(shù)站叼,例如上面的例子,我們可以在 定義函數(shù) 時(shí)菇民,指定三個(gè)String
的默認(rèn)值尽楔,而在 調(diào)用函數(shù) 的時(shí)候投储,如果沒(méi)有傳遞這些參數(shù),那么將會(huì)采用默認(rèn)值:
運(yùn)行結(jié)果為:
但是要注意:
- 如果使用常規(guī)的調(diào)用語(yǔ)法時(shí)阔馋,必須按照函數(shù)聲明中定義的參數(shù)順序來(lái)給定參數(shù)玛荞,可以省略的只有排在末尾的參數(shù)。
-
如果使用命名參數(shù)呕寝,可以省略中間的一些參數(shù)勋眯,也可以以你想要的任意順序只給定你需要的參數(shù),例如我們只修改前綴和后綴壁涎,分隔符仍然采用默認(rèn)值:
運(yùn)行結(jié)果為:
3.3 頂層函數(shù)和屬性
在Java
中凡恍,所有的代碼都需要寫(xiě)作類的函數(shù),但是在項(xiàng)目中怔球,很多代碼并不能歸屬到類中嚼酝,這時(shí)候我們一般會(huì)定義一個(gè)xxUtils
類,并在其中聲明static
的靜態(tài)方法竟坛。
在kotlin
中闽巩,我們可以把這些函數(shù)直接放到代碼文件的頂層,不用從屬于任何的類担汤,這些放在文件頂層的函數(shù)仍然是包內(nèi)的成員涎跨,如果你需要從包外訪問(wèn)它,則需要import
崭歧,但不再需要額外包一層隅很。
3.3.1 在 Java 中調(diào)用頂層函數(shù)
如果我們想要在Java
中調(diào)用這些頂層函數(shù),則需要通過(guò)Kotlin
根據(jù)包含函數(shù)的文件的名稱生成的類率碾,例如我們有兩個(gè)文件
-
KotlinMethod.kt
:
fun kotlinFunc() {}
-
JavaCallKotlin.java
:
public class JavaCallKotlin {
public JavaCallKotlin() {
KotlinMethodKt.kotlinFunc();
}
}
前者包含一個(gè)頂層函數(shù)叔营,那么在Java
中,就會(huì)根據(jù)該文件名生成一個(gè){文件名}Kt
的類型所宰,再通過(guò)這個(gè)類來(lái)調(diào)用這個(gè)頂層函數(shù)绒尊。
如果不想使用默認(rèn)的類名,可以在.kt
文件的開(kāi)頭加上@file:JvmName("類名")
的注解仔粥。
3.3.2 在 Java 中調(diào)用頂層屬性
和函數(shù)一樣婴谱,屬性也可以放到文件的頂層。默認(rèn)情況下躯泰,頂層屬性和其他任意的屬性一樣谭羔,是通過(guò)訪問(wèn)器暴露給Java
使用的(如果是val
就只有一個(gè)getter
,如果是var
就對(duì)應(yīng)getter
和setter
)斟冕。如果想要把一個(gè)常量以public static final
的屬性暴露給Java
口糕,可以使用const
來(lái)修飾它。
package com.demo.lizejun.kotlinsample.chapter1
//不可變磕蛇。
val kotlinVal = "kotlinValue"
//可變景描。
var kotlinVar = "kotlinVariable"
//常量十办。
const val kotlinConst = "kotlinConst"
//頂層函數(shù)。
fun kotlinFunc() {}
在Java
中超棺,分別通過(guò)以下幾種方式來(lái)訪問(wèn)或者修改這幾個(gè)頂層屬性:
package com.demo.lizejun.kotlinsample.chapter3;
import com.demo.lizejun.kotlinsample.chapter1.KotlinMethodKt;
public class JavaCallKotlin {
public JavaCallKotlin() {
KotlinMethodKt.kotlinFunc();
//不可變向族。
KotlinMethodKt.getKotlinVal();
//可變。
KotlinMethodKt.setKotlinVar("newKotlinVar");
KotlinMethodKt.getKotlinVar();
//常量棠绘。
String kotlinConst = KotlinMethodKt.kotlinConst;
}
}
四件相、擴(kuò)展函數(shù)和屬性
擴(kuò)展函數(shù) 其實(shí)是一個(gè)類的成員函數(shù),只不過(guò)它定義在類的外面氧苍,我們所需要做的夜矗,就是在聲明擴(kuò)展函數(shù)的時(shí)候,把需要擴(kuò)展的類或者接口的名稱让虐,放到它的前面紊撕,用來(lái)調(diào)用這個(gè)擴(kuò)展函數(shù)的對(duì)象,就叫做 接收者對(duì)象赡突。
在擴(kuò)展函數(shù)中对扶,可以直接訪問(wèn)被擴(kuò)展的類的其它方法和屬性,就好像是在這個(gè)類自己的方法中訪問(wèn)它們的一樣惭缰,但是擴(kuò)展函數(shù)不允許你打破它的封裝性浪南,擴(kuò)展函數(shù)不能訪問(wèn)私有的或者是受保護(hù)的成員。
4.1 擴(kuò)展函數(shù)的定義和使用
下面我們給String
類添加一個(gè)擴(kuò)展函數(shù)漱受,返回它的最后一個(gè)字符:
運(yùn)行結(jié)果為:
4.2 在 Java 中使用擴(kuò)展函數(shù)
如果需要在Java
中調(diào)用擴(kuò)展函數(shù)络凿,那么把接收者對(duì)象作為第一個(gè)參數(shù)傳進(jìn)去即可:
//接收者對(duì)象作為第一個(gè)參數(shù)。
char lastChar = KotlinMethodKt.last("Kotlin");
4.3 不能重寫(xiě)的擴(kuò)展函數(shù)
這里假設(shè)我們有兩個(gè)類昂羡,View
和Button
喷众,其中Button
繼承于View
,我們給這兩個(gè)類都添加一個(gè)名為showOff
的擴(kuò)展函數(shù)紧憾。
運(yùn)行結(jié)果為:
盡管實(shí)際上這個(gè)變量是一個(gè)
Button
的對(duì)象,但是Kotlin
會(huì)把擴(kuò)展函數(shù)當(dāng)做靜態(tài)函數(shù)來(lái)對(duì)待昌渤,因此 擴(kuò)展函數(shù)不存在重寫(xiě)赴穗。
4.4 擴(kuò)展屬性
擴(kuò)展屬性提供了一種方法,用來(lái)擴(kuò)展類的API
膀息,可以用來(lái)訪問(wèn)屬性般眉,用的是屬性語(yǔ)法而不是函數(shù)的語(yǔ)法,盡管他們被稱為屬性潜支,但它們沒(méi)有任何狀態(tài)甸赃,因?yàn)闆](méi)有合適的地方來(lái)存儲(chǔ)它們。
現(xiàn)在冗酿,我們給StringBuilder
添加一個(gè)可讀寫(xiě)的屬性lastChar
埠对,用于獲取或者改變它的最后一個(gè)字符络断,包含以下幾點(diǎn)要素:
- 擴(kuò)展屬性以
var/val
關(guān)鍵字開(kāi)頭 - 指定擴(kuò)展屬性的名字、類型
- 如果是
var
那么提供get()/set(value : T)
方法项玛,而如果是val
屬性貌笨,那么提供get()
方法,其中T
為屬性的類型襟沮。
運(yùn)行結(jié)果為:
五锥惋、可變參數(shù)、中綴調(diào)用和庫(kù)的支持
5.1 可變參數(shù)
使用關(guān)鍵字 vararg开伏,可以用來(lái)聲明一個(gè)函數(shù)將有可能有任意數(shù)量的參數(shù)膀跌,下面例子中,我們定義一個(gè)可以接收可變數(shù)量Int
類型的函數(shù)固灵,之后和在Java
中一樣捅伤,它會(huì)被轉(zhuǎn)換為[I
的整型數(shù)組類型:
運(yùn)行結(jié)果為:
而如果我們已經(jīng)將參數(shù)打包成一個(gè)數(shù)組,這時(shí)候如果想要將它傳遞給一個(gè)接收可變參數(shù)的函數(shù)怎虫,那么需要先通過(guò)
*
操作符進(jìn)行解包:運(yùn)行結(jié)果為:
5.2 中綴調(diào)用
中綴調(diào)用 不是特殊的內(nèi)置結(jié)構(gòu)暑认,而是一種特殊的函數(shù)調(diào)用。在中綴調(diào)用中大审,沒(méi)有添加額外的分隔符蘸际,函數(shù)名稱是直接放在目標(biāo)對(duì)象名稱和參數(shù)之間。例如我們聲明了一個(gè)to
函數(shù)徒扶。
//一般函數(shù)調(diào)用粮彤。
1.to("One")
//中綴調(diào)用。
1 to "One"
中綴調(diào)用可以與 只有一個(gè)參數(shù)的函數(shù) 一起使用姜骡,無(wú)論是普通的函數(shù)還是擴(kuò)展函數(shù)导坟,要允許使用中綴符號(hào)調(diào)用函數(shù),需要使用infix
修飾符來(lái)標(biāo)記它圈澈,下面是一個(gè)創(chuàng)建Person
的函數(shù)惫周,我們采用了擴(kuò)展函數(shù)的方法,這里的Any
是Kotlin
中所有類的父類康栈,和Java
中的Object
相同:
運(yùn)行的結(jié)果為:
六递递、字符串處理
6.1 分割字符串
在Java
中,我們會(huì)使用split
來(lái)分割字符串啥么,它接受一個(gè)正則表達(dá)式作為參數(shù)登舞。但是當(dāng)我們想分割"."
時(shí),會(huì)得到一個(gè)空的數(shù)組悬荣,因?yàn)?code>.號(hào)表示任何字符的正則表達(dá)式菠秒。
而在kotlin
中,它提供了一個(gè)接受Regex
類型的重載函數(shù)氯迂,這樣確保了當(dāng)有一個(gè)字符串傳遞給這些函數(shù)的時(shí)候践叠,不會(huì)被當(dāng)做正則表達(dá)式言缤,我們可以使用擴(kuò)展函數(shù)toRegex
將字符串轉(zhuǎn)換為正則表達(dá)式。
運(yùn)行結(jié)果為:
6.2 正則表達(dá)式
假設(shè)有下面這個(gè)字符串
/Users/yole/kotlin-book/chapter.adoc
我們需要通過(guò)這個(gè)字符串獲取到chapter.adoc
的目錄酵熙、文件名和擴(kuò)展名轧简,如果使用擴(kuò)展函數(shù),那么代碼如下:
運(yùn)行結(jié)果為:
下面是使用正則表達(dá)式的做法:
這個(gè)正則表達(dá)式將一個(gè)路徑分為三個(gè)由斜線和點(diǎn)分隔的組:
-
.
模式從字符串的一開(kāi)始就進(jìn)行匹配匾二,所以第一組(.+)
包含最后一個(gè)斜線之前的子串哮独,這和子串包含所有前面的斜線,因?yàn)樗鼈兤ヅ洹比魏巫址暗哪J健?/li> - 第二組包含最后一個(gè)點(diǎn)之前的子串
- 第三組包含剩余部分
七察藐、局部函數(shù)
在Java
的一個(gè)函數(shù)當(dāng)中皮璧,有可能存在重復(fù)代碼,例如在注冊(cè)模塊中分飞,可能需要校驗(yàn)輸入的多個(gè)字段是否有效悴务,那么校驗(yàn)的邏輯就可以提取出一個(gè)函數(shù),而Kotlin
就提供了一種方法:可以在函數(shù)中嵌套這些提取的函數(shù)譬猫,局部函數(shù)定義方式和普通函數(shù)是相同的讯檐。
運(yùn)行的結(jié)果為:
更多文章,歡迎訪問(wèn)我的 Android 知識(shí)梳理系列:
- Android 知識(shí)梳理目錄:http://www.reibang.com/p/fd82d18994ce
- 個(gè)人主頁(yè):http://lizejun.cn
- 個(gè)人知識(shí)總結(jié)目錄:http://lizejun.cn/categories/