Kotlin概述
Kotlin是由開發(fā)過IntelliJ IDEA、Android Studio旧蛾、PyCharm等IDE的著名IDE廠商JetBrains公司設(shè)計并開源的編程語言。2011年7月推出的Kotlin項目深受《Effective Java》的影響褐着,直到2016年2月15日第一個官方穩(wěn)定版本Kotlin v1.0才正式發(fā)布,2017年Google I/O開發(fā)者大會中汗唱,Google宣布Kotlin成為Android開發(fā)的一級語言谷饿,Kotlin “轉(zhuǎn)正”未荒。
Kotlin是一種運(yùn)行在JVM上的靜態(tài)類型編程語言,可以編譯為Java字節(jié)碼接箫,同時也可以編譯成JavaScript攒读、本地(Native)代碼,方便在沒有JVM的設(shè)備上運(yùn)行辛友。Kotlin語言具有以下特點(diǎn):
- 與Java的編譯薄扁、運(yùn)行速度相似
- 比Java更安全、簡潔
- 比最成熟的競爭者Scala更簡潔
Kotlin在語法上具有很多下一代編程語言靜態(tài)語言特性:如類型推斷废累、函數(shù)式編程邓梅、多范式支持、可空性表達(dá)邑滨、擴(kuò)展函數(shù)日缨、模式匹配等。
Kotlin與Java具有良好的兼容性掖看,與Java高度可互操作匣距,在同一項目的開發(fā)中可以同時使用兩種語言進(jìn)行編寫(僅限于不同文件中,不能在同一文件中使用兩種語言)哎壳,如Kotlin可以直接調(diào)用Java的代碼毅待,而Java調(diào)用Kotlin需要使用一些注解,但也不是很復(fù)雜归榕。IntelliJ IDEA提供了Java代碼到Kotlin代碼的轉(zhuǎn)換功能尸红,您只需將Java代碼拷貝粘貼到.kt文件中,IDE就會自動將其轉(zhuǎn)換為Kotlin代碼,方便Java學(xué)習(xí)者順利過渡到Kotlin驶乾。
Kotlin支持像Python一樣的REPL環(huán)境邑飒,可以很方便的進(jìn)行代碼測試,對于語言的學(xué)習(xí)十分有幫助级乐,配置環(huán)境變量后只需要在命令行輸入kotlinc疙咸,即可開啟REPL環(huán)境。
這篇文章面向有Java語言基礎(chǔ)的對Kotlin有興趣的讀者风科,內(nèi)容比較基礎(chǔ)撒轮,主要通過兩種語言的對比進(jìn)行說明。
基礎(chǔ)語法
Hello World
如圖所示贼穆,使用IntelliJ IDEA新建項目時可選擇Java题山,同時在右側(cè)勾選Kotlin/JVM,或者直接新建Kotlin項目故痊。
fun main(args: Array<String>) {
println("hello world")
}
上述Kotlin代碼的作用就是輸出hello world
顶瞳,看起來比Java等語言簡單得多,Kotlin中通過fun
關(guān)鍵字聲明一個函數(shù)愕秫,main
是函數(shù)名慨菱,在這里是應(yīng)用程序的入口;args
是參數(shù)戴甩,這里代表命令行參數(shù)符喝,它的類型是字符串?dāng)?shù)組,需要注意的是Kotlin的變量名通常寫在類型的前面甜孤,中間用冒號分開协饲。
println()
是Kotlin中的頂層函數(shù),對應(yīng)Java中的System.out.println()
函數(shù)缴川,頂層函數(shù)不屬于任何類茉稠,可以直接拿來用,類似用法的頂層函數(shù)還有print()
等把夸。
Kotlin的語句最后的分號不是必須的而线,只有當(dāng)多個語句寫在同一行時,才必須用分號加以區(qū)分扎即。
變量和數(shù)據(jù)類型
常量和變量
var a = 1
const val b = 2
fun main(args: Array<String>) {
a = 2
b = 1 //編譯錯誤
const val c = 3 //編譯錯誤
c = 1 //編譯錯誤
val d = 4
d = 1 //編譯錯誤
var e: Float = 5.0f
e = 1
}
//聲明對象,對應(yīng)Java中需要通過構(gòu)造方法私有化實現(xiàn)的單例
object Test {
const val TEST = 100
}
Kotlin中聲明變量通常使用val/var 變量名: 變量類型 = 表達(dá)式
况凉,如上述代碼中的變量e
谚鄙,Kotlin語言支持變量類型的自動推斷,通常都不需要顯式的聲明變量類型刁绒。
Kotlin變量分為var
(可變變量)和val
(只讀變量闷营,也稱不可變變量、運(yùn)行期常量),其中var
是可寫的傻盟,在它的生命周期中可以被多次賦值速蕊,如上述代碼中的a
和e
;而val
是只讀的娘赴,它是在運(yùn)行時初始化的规哲,但僅能賦值一次,如對d
重新賦值會發(fā)生編譯錯誤诽表,只讀變量相當(dāng)于Java中用final
修飾的變量(并不完全相同)唉锌。只讀變量的值只能被修改一次,并且不能被覆蓋竿奏,這可以避免變量的值被錯誤的修改袄简。
Kotlin的常量(編譯期常量)用const val
聲明,僅能用于頂層常量和對象中的常量聲明泛啸,如上述代碼中的b
和Test
對象中的TEST
常量绿语,在函數(shù)中聲明的c
則會發(fā)生編譯錯誤,編譯期常量相當(dāng)于Java中用public final static
修飾的常量(并不完全相同)候址,Kotlin中的常量只能是String類型或基本數(shù)據(jù)類型吕粹。
基本數(shù)據(jù)類型
Kotlin基本數(shù)據(jù)類型 | Java基本數(shù)據(jù)類型 | Java包裝類 |
---|---|---|
Int | int | java.lang.Integer |
Long | long | java.lang.Long |
Float | float | java.lang.Float |
Double | double | java.lang.Double |
Short | short | java.lang.Short |
Char | char | java.lang.Character |
Byte | byte | java.lang.Byte |
Boolean | boolean | java.lang.Boolean |
下表展示了Kotlin中的8種基本數(shù)據(jù)類型對應(yīng)的Java基本數(shù)據(jù)類型和Java包裝類:
Kotlin基本數(shù)據(jù)類型 | Java基本數(shù)據(jù)類型 | Java包裝類 |
---|---|---|
Int | int | java.lang.Integer |
Long | long | java.lang.Long |
Float | float | java.lang.Float |
Double | double | java.lang.Double |
Short | short | java.lang.Short |
Char | char | java.lang.Character |
Byte | byte | java.lang.Byte |
Boolean | boolean | java.lang.Boolean |
與Java不同,Kotlin的字符類型(Char
)不屬于數(shù)值類型宗雇,Kotlin中的基本數(shù)據(jù)類型沒有對應(yīng)的包裝類昂芜,編譯器會視具體情況將其編譯為Java基本數(shù)據(jù)類型或者包裝類對象。
val a = 100L //定義Long類型的變量a赔蒲,L后綴(不允許使用l)表示Long類型
val b = 123_456_789L //通過_可以分割數(shù)字泌神,使其更清晰
val c = 0B10010 //0b或0B前綴表示二進(jìn)制數(shù)
val d = 0x1A //0x或0X前綴表示十六進(jìn)制數(shù)
val pi = 3.14159 //pi默認(rèn)是Double類型(不允許使用后綴d或D),不是Float類型
val e = 2.71828f //f或F后綴表示Float類型
上述代碼演示了Kotlin中數(shù)值類型的字面量舞虱,其中比較特殊的是:
- Kotlin的小數(shù)默認(rèn)是
Double
類型欢际,不能加D
或d
后綴 - 為了避免小寫字母
l
和數(shù)字1
的混淆,Kotlin不允許使用小寫字母l
作為Long類型整數(shù)的后綴矾兜,必須使用大寫字母L
- 使用
_
進(jìn)行比較長的數(shù)字的分割時损趋,不強(qiáng)制要求每3位數(shù)字分割一次
非空類型與可空類型
為了避免Java中經(jīng)常出現(xiàn)的空指針異常(NullPointerException
),Kotlin的類型默認(rèn)定義為非空椅寺,即不可接收空值(null
)浑槽,直接對非空類型變量賦值null
會發(fā)生編譯錯誤。當(dāng)我們確認(rèn)需要可空類型時返帕,可以通過在非空類型后面加一問號(?
)桐玻,此時的變量可以接收空值,如:
val a: Int = null //編譯錯誤
val b: Int? = null
非空類型永遠(yuǎn)不可能為null荆萤,而可空類型則存在潛在的空指針風(fēng)險镊靴,Kotlin不允許可空類型對象直接調(diào)用非空類型對象的屬性或函數(shù)铣卡,也不能把可空類型數(shù)據(jù)賦值給非空類型變量或是傳遞給非空類型參數(shù)。為了使可空類型能夠調(diào)用非空類型的方法偏竟,Kotlin提供了以下幾個運(yùn)算符:
- 非空斷言運(yùn)算符:
!!
- 安全調(diào)用運(yùn)算符:
?.
- 安全轉(zhuǎn)換運(yùn)算符:
as?
- Elvis運(yùn)算符(空值合并運(yùn)算符):
?:
下面對這幾個運(yùn)算符進(jìn)行介紹:
- 非空斷言運(yùn)算符
!!
:非空斷言運(yùn)算符用于斷言一個可空類型變量不為空值煮落,使用這個運(yùn)算符會存在拋出空指針異常(kotlin.KotlinNullPointerException
)的風(fēng)險。
fun main(args: Array<String>) {
var a: String? = "abc"
println(a!!.length) //輸出3
val b: String? = null
b!! //拋出空指針異常
}
上述代碼由于對值為空的變量b
進(jìn)行了非空斷言踊谋,運(yùn)行時會拋出空指針異常蝉仇。在最新版的Kotlin中,若已經(jīng)檢查了變量非空褪子,則允許不使用非空斷言運(yùn)算符直接調(diào)用非空類型的屬性和函數(shù)量淌。
- 安全調(diào)用運(yùn)算符
?.
:該運(yùn)算符用于對可空類型變量安全的調(diào)用非空類型的屬性或函數(shù),而不會拋出空指針異常嫌褪,當(dāng)對象為空時呀枢,直接返回空值(null
),否則進(jìn)行調(diào)用并返回結(jié)果笼痛。
fun main(args: Array<String>) {
var a: String? = "abc"
println(a?.length) //輸出3
val b: String? = null
println(b?.length) //輸出null
}
它等價于以下代碼:
fun main(args: Array<String>) {
var a: String? = "abc"
println(a?.length)
val b: String? = null
if(b == null)
println(null)
else
println(b!!.length) //在最新版的Kotlin中此處的!!可以省略
}
安全轉(zhuǎn)換運(yùn)算符
as?
:在Kotlin中使用as
和as?
進(jìn)行強(qiáng)制類型轉(zhuǎn)換裙秋,這部分內(nèi)容將在面向?qū)ο蟮亩鄳B(tài)部分進(jìn)行說明。Elvis運(yùn)算符
?:
:該運(yùn)算符名稱的由來是?:
像“貓王”(美國搖滾歌手)埃爾維斯·普雷斯利(Elvis Presley)的頭型和眼睛缨伊,其作用是空值合并摘刑。該運(yùn)算符是一個二元運(yùn)算符(注意Kotlin不存在條件 ? 邏輯表達(dá)式1 : 邏輯表達(dá)式2
這個三元運(yùn)算符),語法是可空類型數(shù)據(jù) ?: 空值合并到的數(shù)據(jù)
刻坊,作用是當(dāng)數(shù)據(jù)非空時枷恕,直接返回數(shù)據(jù),而當(dāng)數(shù)據(jù)為空時谭胚,返回合并到的數(shù)據(jù)徐块。利用該運(yùn)算符,可以很容易的把可空類型轉(zhuǎn)換為非空類型灾而。
fun main(args: Array<String>) {
var a: String? = "abc"
println(a?.length ?: "字符串為空") //輸出3
val b: String? = null
val c: Int = b?.length ?: -1
println(c) //輸出-1
}
等價于如下代碼:
fun main(args: Array<String>) {
var a: String? = "abc"
println(a?.length ?: "字符串為空")
val b: String? = null
val c: Int
print(c) //編譯錯誤胡控,非空變量初始化前不能取值,請刪掉此行代碼重新運(yùn)行
if(b == null)
c = -1
else
c = b!!.length //在最新版的Kotlin中此處的!!可以省略
println(c)
}
函數(shù)聲明
Kotlin既有獨(dú)立于類的頂層函數(shù)旁趟,也有類方法昼激,為了避免混淆,這里統(tǒng)稱為函數(shù)锡搜。上述main
中演示了參數(shù)的定義橙困,下面的代碼中演示了返回值,返回值應(yīng)在函數(shù)的參數(shù)列表括號之后顯式地聲明耕餐。
fun getMax(a: Int, b: Int): Int {
if (a > b) {
return a
} else {
return b
}
}
Kotlin的函數(shù)(包括頂層函數(shù)和類函數(shù))可以帶有參數(shù)默認(rèn)值凡傅,而Java中想要實現(xiàn)類似的功能則需要冗長的方法重載或使用builder模式。
fun main(args: Array<String>) {
printPerson("小明", age = 18)
}
fun printPerson(name: String, sex: Boolean = true, age: Int): Unit {
println("姓名:" + name)
println("性別:" + if (sex) "男" else "女")
println("年齡:" + age)
}
上述代碼中對參數(shù)sex
使用了默認(rèn)值true
蛾方,在調(diào)用函數(shù)不提供參數(shù)時其值為true
像捶,調(diào)用時使用了命名參數(shù)age
,采用命名參數(shù)可以增加代碼的可讀性桩砰,參數(shù)順序可以與函數(shù)定義時不一致拓春,建議帶有默認(rèn)值的參數(shù)放在參數(shù)列表的最后面,需要注意的是在調(diào)用函數(shù)時一旦其中一個參數(shù)采用了命名參數(shù)亚隅,其后所有的參數(shù)都必須采用命名參數(shù)形式傳遞硼莽。上述代碼中函數(shù)的返回值類型定義為Unit
,相當(dāng)于Java中的void
類型煮纵,此處可以省略返回值類型懂鸵。
字符串模板
原始字符串:使用三個雙引號包起來的字符串,原始字符串中的\
將不被識別為轉(zhuǎn)義行疏,字符串中可以包含換行匆光,如:
"""hello
word"""
下面的代碼是使用原始字符串輸出三行內(nèi)容,第2酿联、3行都在行首開始终息,這樣的代碼有時看起來對齊的并不自然。
fun main(args: Array<String>) {
println(getText())
}
fun getText(): String {
return """Kotlin is created by JetBrains
Kotlin is a Programming Language running on JVM
We can use Kotlin to create Android apps"""
}
可以使用trimMargin()
贞让、trimIndent()
裁剪函數(shù)來去除前導(dǎo)空格周崭。
fun getText(): String {
return """ Kotlin is created by JetBrains
Kotlin is a Programming Language running on JVM
We can use Kotlin to create Android apps""".trimIndent()
}
String類型的函數(shù)(方法)trimIndent()
用于切割每一行開頭相同數(shù)量的空格。
fun getText(): String {
return """Kotlin is created by JetBrains
#Kotlin is a Programming language running on JVM
#We can use Kotlin to create Android apps""".trimMargin("#")
}
trimMargin()
函數(shù)用于從字符串中每行的開頭裁剪指定的字符串參數(shù)以及前面的全部空格喳张,如果不提供參數(shù)续镇,則以|
作為參數(shù)默認(rèn)值。
Kotlin還支持類似于PHP中的字符串模板销部,利用這一特性摸航,前面提供的函數(shù)可以進(jìn)行如下改寫:
fun printPerson(name: String, sex: Boolean = true, age: Int): Unit {
println("姓名:$name")
println("性別:${if (sex) "男" else "女"}")
println("年齡:$age")
}
在原始字符串和一般的字符串中都允許使用字符串模板,字符串模板能夠簡化字符串拼接的操作柴墩,字符串模板是以$
開頭忙厌,其語法為$變量或常量名
或${表達(dá)式}
,如果想要在字符串中使用$
符號江咳,請用${'$'}
或\$
代替逢净。
流程控制
if語句/表達(dá)式
Kotlin簡化了Java中的一些語句,將其變?yōu)榫哂蟹祷刂档谋磉_(dá)式歼指,主要有if
表達(dá)式爹土、when
表達(dá)式、try
表達(dá)式踩身、表達(dá)式函數(shù)體胀茵,示例代碼如下:
fun main(args: Array<String>) {
var a = 1
var b = 2
//if表達(dá)式,可以自動推斷出max的類型是Int挟阻,不需要顯式聲明
val max = if(a > b) a else b
//對可變變量重新賦值
a = 5
b = 3
//調(diào)用下面定義的getMin函數(shù)
val min = getMin(a, b)
}
//表達(dá)式函數(shù)體琼娘,返回值是執(zhí)行到的語句塊的最后一條語句
fun getMin(a: Int, b: Int) = if(a > b) {
println("b < a")
b //if表達(dá)式的返回值
} else {
println("a < b")
a //if表達(dá)式的返回值
}
when語句/表達(dá)式
Kotlin放棄了C和Java中的switch
語句峭弟,采用一個新的when
語句/表達(dá)式,相比switch
語句脱拼,when
語句/表達(dá)式要更加的強(qiáng)大瞒瘸、靈活。
when
是一個多分支的結(jié)構(gòu)熄浓,像if
一樣情臭,when
的每一個分支都可以是一個代碼塊,when
表達(dá)式也具有返回值赌蔑,它的值是塊中最后的表達(dá)式的值俯在。可以用任意表達(dá)式(不只是常量)作為分支條件娃惯,也可以把多個分支條件通過逗號放在一起跷乐,還可以用in
和!in
加上一個區(qū)間檢測一個值是否在一個區(qū)間或者集合中、用is
和!is
加上一個類名判斷是否是某個類的實例(Java中對應(yīng)的是instanceof
)趾浅,如果所有分支都不滿足劈猿,則會執(zhí)行else
分支(可以沒有)。
fun main(args: Array<String>) {
val score = 98
val result = when (score) {
97, 98, 99 -> "接近滿分了"
in 90..96, 100 -> {
println("優(yōu)")
"優(yōu)秀"
}
in 60..89 -> "及格"
in 0..59 -> "不及格"
else -> {
println("成績輸入不正確")
null
}
}
}
上述代碼中的..
是區(qū)間運(yùn)算符潮孽,如0..59
是生成閉區(qū)間[0, 59]內(nèi)的整數(shù)數(shù)列揪荣,每個區(qū)間是一個可迭代的對象,Kotlin還提供了一個半開區(qū)間運(yùn)算符until
往史,如0 until 10
則是生成半開區(qū)間[0,10)內(nèi)的整數(shù)仗颈,Kotlin核心庫中共提供了3個閉區(qū)間類:IntRange
、LongRange
和CharRange
椎例。
循環(huán)語句
Kotlin具有和C挨决、Java類似的do-while
循環(huán)和while
循環(huán),這里不再贅述订歪,主要介紹for
循環(huán)脖祈。下面演示一個打印乘法口訣表的代碼:
fun main(args: Array<String>) {
for(i in 1..9) {
for(j in 1..i)
print("$j×$i=${i * j}\t")
println() //另起一行
}
}
Kotlin的for
循環(huán)相當(dāng)于Java的foreach
循環(huán),但for
循環(huán)中的變量無需用var
或val
聲明刷晋,for-in
循環(huán)可用于對范圍或者集合進(jìn)行遍歷盖高。結(jié)合生成遞減數(shù)列的中綴運(yùn)算符,可以實現(xiàn)更復(fù)雜的功能眼虱。
fun main(args: Array<String>) {
for(i in 15 downTo 1 step 3)
println(i)
}
上述代碼中的downTo
是生成一個遞減數(shù)列喻奥,而step
則是設(shè)置數(shù)列的步長。
帶標(biāo)簽的break和continue
與Java類似捏悬,Kotlin也支持帶標(biāo)簽的break和continue語句撞蚕,語法是在for
、while
或do
前面加上標(biāo)簽名@
过牙,使用標(biāo)簽時用break/continue@標(biāo)簽名
甥厦,如:
fun main(args: Array<String>) {
label1@ for (x in 0..10) {
for (y in 10 downTo 5 step 3) {
if(x % 2 == 0) continue@label1
if(y == x) break@label1
println("(x, y)=($x, $y)")
}
}
}
Kotlin運(yùn)算符
算術(shù)運(yùn)算符纺铭、邏輯運(yùn)算符和關(guān)系運(yùn)算符
Kotlin的算術(shù)運(yùn)算符、邏輯運(yùn)算符與Java一致刀疙,關(guān)系運(yùn)算符與Java稍有不同彤蔽,其中==
運(yùn)算符對兩個對象進(jìn)行比較,調(diào)用的是對象的equals()
函數(shù)庙洼,!=
則是對==
運(yùn)算符的取反;而對引用類型的比較則需使用===
和!==
運(yùn)算符镊辕,用于判斷兩個引用是否指向同一個對象油够。當(dāng)一個對象與null
進(jìn)行顯式的比較時,使用兩種運(yùn)算符效果相同征懈,Kotlin會自動將==
轉(zhuǎn)換為===
石咬。
位運(yùn)算符
名稱 | Java位運(yùn)算符 | Kotlin位運(yùn)算符 | 示例 |
---|---|---|---|
位取反 | ~ | inv | a.inv() |
位與 | & | and | a and b 或 a.and(b) |
位或 | | | or | a or b 或 a.or(b) |
位異或 | ^ | xor | a xor b 或 a.xor(b) |
左移 | << | shl | a shl b 或 a.shl(b) |
有符號右移 | >> | shr | a shr b 或 a.shr(b) |
無符號右移 | >>> | ushr | a ushr b 或 a.ushr(b) |
Java和Kotlin的位運(yùn)算符和Kotlin位運(yùn)算符的示例如下表所示:
名稱 | Java位運(yùn)算符 | Kotlin位運(yùn)算符 | 示例 |
---|---|---|---|
位取反 | ~ | inv | a.inv() |
位與 | & | and | a and b 或 a.and(b) |
位或 | | | or | a or b 或 a.or(b) |
位異或 | ^ | xor | a xor b 或 a.xor(b) |
左移 | << | shl | a shl b 或 a.shl(b) |
有符號右移 | >> | shr | a shr b 或 a.shr(b) |
無符號右移 | >>> | ushr | a ushr b 或 a.ushr(b) |
其他運(yùn)算符
前面的介紹中提到了非空斷言運(yùn)算符!!
、安全調(diào)用運(yùn)算符?.
卖哎、安全轉(zhuǎn)換運(yùn)算符as?
鬼悠、Elvis運(yùn)算符?:
等,Kotlin的y一些運(yùn)算符是通過調(diào)用轉(zhuǎn)換方法來實現(xiàn)的亏娜,下表介紹了一些運(yùn)算符及其轉(zhuǎn)換方法:
- 一元運(yùn)算符
名稱 | 運(yùn)算符 | 轉(zhuǎn)換方法 | 作用 |
---|---|---|---|
+a | a.unaryPlus() | ||
-a | a.unaryMinus() | ||
!a | a.not() | ||
自增 | a++或++a | a.inc() | |
自減 | a--或--a | a.dec() | |
引用 | :: | 引用類焕窝、屬性或函數(shù) | |
展開 | * | 用于將數(shù)組傳遞給可變參數(shù) |
- 二元運(yùn)算符
名稱 | 運(yùn)算符 | 轉(zhuǎn)換方法 | 作用 |
---|---|---|---|
相加 | a + b | a.plus(b) | |
相減 | a - b | a.minus(b) | |
相乘 | a * b | a.times(b) | |
相除 | a / b | a.div(b) | |
取余 | a % b | a.mod(b) | |
區(qū)間 | a..b | a.rangeTo(b) | |
比較 | >、<维贺、>=它掂、<= | a.compareTo(b) | 比較兩個對象,通過返回值的正負(fù)判斷大小 |
a in b | b.contains(a) | 判斷a是否在b中 | |
a !in b | !b.contains(a) | ||
相等 | a == b | a?.equals(b) ?: (b === null) | |
不等 | a != b | !(a?.equals(b) ?: (b === null)) | |
調(diào)用 | a(i) | a.invoke(i) | |
調(diào)用 | a(i, j) | a.invoke(i, j) | |
調(diào)用 | a(i_1, … , i_n) | a.invoke(i_1, …, i_n) | |
索引訪問 | a[i] | a.get(i) | |
索引訪問 | a[i, j] | a.get(i, j) | |
索引訪問 | a[i_1, …, i_n] | a.get(i_1, …, i_n) | |
索引訪問 | a[i] = b | a.set(i, b) | |
索引訪問 | a[i, j] = b | a.set(i, j, b) | |
索引訪問 | a[i_1, …, i_n] = b | a.set(i_1, …, i_n, b) |
局部函數(shù)和匿名函數(shù)
Kotlin的函數(shù)可以聲明在類內(nèi)部溯泣,稱為成員函數(shù)虐秋,還可以聲明在另一函數(shù)的內(nèi)部,稱為局部函數(shù)垃沦。當(dāng)函數(shù)只被調(diào)用一次時客给,可以聲明為匿名函數(shù)。
fun main(args: Array<String>) {
println(calculate(1.0, 2.0))
println(calculate(1.0, 2.0, '-'))
println(calculate(1.0, 2.0,'*'))
println(calculate(1.0, 2.0, '/'))
println(calculate(2.0, 1.0, '%'))
}
fun calculate(n1: Double, n2: Double, operator: Char = '+'): Double {
//resultFun是一個函數(shù)類型的變量肢簿,
val resultFun: (Double, Double) -> Double = when (operator) {
'+' -> fun(a: Double, b: Double): Double { return a + b }
'-' -> fun(a, b) = a - b
'*' -> { a: Double, b: Double -> a * b }
'/' -> { a, b -> a / b }
else -> { _, _ ->
println("輸入數(shù)據(jù)錯誤")
0.0
}
}
return resultFun(n1, n2)
}
上述代碼中使用了when
表達(dá)式靶剑、參數(shù)默認(rèn)值和命名參數(shù)、函數(shù)類型變量池充、匿名局部函數(shù)抬虽、表達(dá)式函數(shù)體、Lambda表達(dá)式和類型推斷纵菌。
Kotlin支持函數(shù)類型阐污,可以定義函數(shù)類型的變量,類似C語言中的函數(shù)指針咱圆,允許把函數(shù)作為參數(shù)傳遞笛辟,這是Kotlin對Java反射機(jī)制的增強(qiáng)實現(xiàn)功氨,反射機(jī)制內(nèi)容較多,這里只對函數(shù)類型進(jìn)行說明:函數(shù)類型的格式為:(參數(shù)數(shù)據(jù)類型列表) -> 返回值類型
手幢,例如:() -> Unit
表示空參數(shù)列表無返回值的函數(shù)類型捷凄,(Double, Double) -> Double
表示兩個浮點(diǎn)型參數(shù),返回值為浮點(diǎn)型的函數(shù)類型围来。
上述代碼五個分支全部使用了匿名局部函數(shù)跺涤,其中"+"
分支是省略了函數(shù)名(匿名)的局部函數(shù),"-"
分支進(jìn)一步將使用表達(dá)式函數(shù)體簡化了函數(shù)體并省略參數(shù)和返回值類型的聲明(Kotlin會根據(jù)定義的接收函數(shù)的變量的類型進(jìn)行推斷)监透,"*"
桶错、"/"
和else
分支使用了Lambda表達(dá)式。
Lambda表達(dá)式是一種特殊的匿名函數(shù)胀蛮,它是一個函數(shù)對象院刁,其聲明方式為{ 參數(shù)列表 -> 語句塊 },Lambda表達(dá)式內(nèi)部的返回值是語句塊的最后一行粪狼,Lambda表達(dá)式的參數(shù)列表類型也支持自動推斷退腥,當(dāng)某個參數(shù)在語句塊中沒有用到的時候,可以通過下劃線_
占位再榄,如else
分支狡刘,當(dāng)Lambda只有一個參數(shù)時,可以省略參數(shù)困鸥,只寫語句塊颓帝,其中唯一的參數(shù)(隱式參數(shù))用it
標(biāo)識符代替。
Kotlin約定,在調(diào)用函數(shù)時,如果使用Lambda表達(dá)式作為參數(shù)堆生,若Lambda表達(dá)式是最后一個參數(shù),可以將Lambda表達(dá)式塊移到參數(shù)列表括號的后面瘪板,而當(dāng)只有這一個參數(shù)時,前面的小括號可以省略漆诽,如:
fun main(args: Array<String>) {
test({ x:Int, y:Int -> x + y })
test() { x, y -> x - y }
test { a, b -> a * b }
}
fun test(block: (a: Int, b: Int) -> Int) {
println(block(1, 2))
}
數(shù)組
基本數(shù)據(jù)類型數(shù)組
Kotlin基本數(shù)據(jù)類型數(shù)組 | Java基本數(shù)據(jù)類型數(shù)組 |
---|---|
IntArray | int[] |
LongArray | long[] |
FloatArray | float[] |
DoubleArray | double[] |
ShortArray | short[] |
ByteArray | byte[] |
CharArray | char[] |
BooleanArray | boolean[] |
Kotlin基本數(shù)據(jù)類型數(shù)組和Java基本數(shù)據(jù)類型數(shù)組的對應(yīng)關(guān)系如下表所示:
Kotlin基本數(shù)據(jù)類型數(shù)組 | Java基本數(shù)據(jù)類型數(shù)組 |
---|---|
IntArray | int[] |
LongArray | long[] |
FloatArray | float[] |
DoubleArray | double[] |
ShortArray | short[] |
ByteArray | byte[] |
CharArray | char[] |
BooleanArray | boolean[] |
Kotlin的數(shù)組都是通過使用頂層函數(shù)創(chuàng)建的侮攀,基本數(shù)據(jù)類型數(shù)組有3種創(chuàng)建方式,以Int
類型為例厢拭,它們的函數(shù)聲明如下:
-
intArrayOf(varag element: Int)
:這里的varag
表示這是一個可變參數(shù)兰英,可變參數(shù)必須位于參數(shù)列表的最后一個,調(diào)用時需要提供零個或多個Int
類型的數(shù)據(jù) -
IntArray(size: Int)
:該函數(shù)通過指定數(shù)組的元素個數(shù)創(chuàng)建數(shù)據(jù)類型默認(rèn)值的數(shù)組供鸠,Int
的默認(rèn)值是0 -
IntArray(size: Int, init: (Int) -> Int)
:該函數(shù)第一個參數(shù)指定數(shù)組大小畦贸,第二個參數(shù)提供一個(Int) -> Int
類型的函數(shù),通常使用Lambda表達(dá)式,函數(shù)的第一個參數(shù)是數(shù)組的下標(biāo)(從0開始)
val a = intArrayOf(1, 0, 0, 8, 6) //產(chǎn)生由1, 0, 0, 8, 6構(gòu)成的基本數(shù)據(jù)類型數(shù)組
val b = intArrayOf(*a, 1, 1) //*是展開運(yùn)算符薄坏,產(chǎn)生由1, 0, 0, 8, 6, 1, 1構(gòu)成的基本數(shù)據(jù)類型數(shù)組
val c = DoubleArray(8) //產(chǎn)生8個0.0構(gòu)成的基本數(shù)據(jù)類型數(shù)組
val d = CharArray(26) { (it + 65).toChar() } //產(chǎn)生26個大寫字母構(gòu)成的基本數(shù)據(jù)類型數(shù)組
d[0] = 'a' //通過調(diào)用set()函數(shù)為數(shù)組單個成員賦值
對象數(shù)組
Kotlin對象數(shù)組 | Java包裝類數(shù)組 |
---|---|
Array<Int> | java.lang.Integer[] |
Array<Long> | java.lang.Long[] |
Array<Float> | java.lang.Float[] |
Array<Double> | java.lang.Double[] |
Array<Short> | java.lang.Short[] |
Array<Byte> | java.lang.Byte[] |
Array<Char> | java.lang.Character[] |
Array<Boolean> | java.lang.Boolean[] |
Kotlin中的對象數(shù)組對應(yīng)Java中的包裝類數(shù)組趋厉,其對應(yīng)關(guān)系為:
Kotlin對象數(shù)組 | Java包裝類數(shù)組 |
---|---|
Array<Int> | java.lang.Integer[] |
Array<Long> | java.lang.Long[] |
Array<Float> | java.lang.Float[] |
Array<Double> | java.lang.Double[] |
Array<Short> | java.lang.Short[] |
Array<Byte> | java.lang.Byte[] |
Array<Char> | java.lang.Character[] |
Array<Boolean> | java.lang.Boolean[] |
對象數(shù)組也有3種創(chuàng)建方式,以Int
類型為例:
-
arrayOf(varag elements: T)
:創(chuàng)建類型為T
的數(shù)組變量胶坠,提供一組相同數(shù)據(jù)類型的數(shù)據(jù)列表 -
arrayOfNulls<T>(size: Int)
:size
參數(shù)用于指定創(chuàng)建數(shù)組的大小君账,該函數(shù)用于創(chuàng)建一個類型為T的數(shù)組,所有的元素均為空值 -
Array(size:Int, init: (Int) -> T)
:該函數(shù)第一個參數(shù)指定數(shù)組大小沈善,第二個參數(shù)提供一個(Int) -> T
類型的函數(shù)乡数,通常使用Lambda表達(dá)式,函數(shù)的第一個參數(shù)是數(shù)組的下標(biāo)(從0開始)
fun main(args: Array<String>) {
val array2D = Array(3) { arrayOfNulls<Int>(5) }
println(array2D.indices) //數(shù)組的indices屬性是數(shù)組的下標(biāo)區(qū)間
println(array2D[0].indices)
//遍歷方式1
for(i in array2D)
for(j in i)
println(j)
//遍歷方式2
for(i in array2D.indices)
for(j in array2D[0].indices)
println(array2D[i][j])
}
需要本教程MarkDown版本的請聯(lián)系QQ:2336233438
轉(zhuǎn)發(fā)請注明原文鏈接