前序
????????在19年的Google I/O大會(huì)上,Kotlin 成為 Android 開(kāi)發(fā)首選語(yǔ)言。而著名的OkHttp 已經(jīng)開(kāi)始用 Kotlin 進(jìn)行重寫(xiě)工作。是時(shí)候通過(guò)寫(xiě)博客歸納來(lái)鞏固Kotlin基礎(chǔ)知識(shí)。
(一)、語(yǔ)法上的變更
- 創(chuàng)建對(duì)象不需要new關(guān)鍵字
- 語(yǔ)句不需要;結(jié)尾,加;也無(wú)所謂
- 變量類型后置,即變量名在前,變量類型在后。例如 str:String
- 使用 println() 替代 System.out.println()
(二)、定義變量
????????Kotlin中使用val關(guān)鍵字聲明常量(即變量初始化之后不可再次賦值),var關(guān)鍵字聲明變量。
變量定義時(shí)可以不顯示指定類型,編譯器會(huì)根據(jù)其初始化器的類型進(jìn)行推斷。
//自動(dòng)推斷出 `Int` 類型
var daqi = 1
//可以顯式地指定變量的類型
val a :String = "daqi"
//如果變量沒(méi)有初始化器,需要顯式地指定它的類型
val c: Int
c = 3
當(dāng)編譯器能確保val變量只有唯一一條初始化語(yǔ)句會(huì)被執(zhí)行,可以根據(jù)條件對(duì)它初始化不同的值。
val daqi:String
var isChange = true
if (isChange){
daqi = "1"
}else{
daqi = "2"
}
val變量的引用自身是不可變的,但是它指向的對(duì)象是可變的。
val languages = arrayListOf("Java")
languages.add("Kotlin")
var 關(guān)鍵字允許變量改變自己的值,但不能改變自己的類型。
var daqi = 1
//編譯不通過(guò)
daqi = ""
(三)、定義函數(shù)
Kotlin 中使用 fun 關(guān)鍵字聲明函數(shù)。
完整的函數(shù)定義:
修飾符(默認(rèn)public)+ fun + 方法名 + (參數(shù)列表) :返回值 {
函數(shù)體
}
public fun daqi(name:String):String {
}
表達(dá)式函數(shù)體
????????當(dāng)單個(gè)表達(dá)式構(gòu)成完整的函數(shù)體時(shí),可以直接去掉 {} 和 return 語(yǔ)句,函數(shù)的返回值類型編譯器也會(huì)自動(dòng)推斷。這種函數(shù)就是表達(dá)式函數(shù)。
fun sum(a: Int, b: Int) = a + b
語(yǔ)句和表達(dá)式的區(qū)別在于:
- 表達(dá)式有值,并且能作為另一個(gè)表達(dá)式的一部分使用;
- 語(yǔ)句總是包圍著它的代碼塊中的頂層元素,并且沒(méi)有自己的值。
????????在 Java 中,所有的控制結(jié)構(gòu)都是語(yǔ)句。而在 Kotlin 中,除了循環(huán)( for, do 和 do/while )以外大多數(shù)控制結(jié)構(gòu)都是表達(dá)式(例如:if、 when 以及 try 屬于表達(dá)式)
???????? 表達(dá)式函數(shù)不光用在些簡(jiǎn)單的單行函數(shù)中 ,也可以用在對(duì)更復(fù)雜的單個(gè)表達(dá)式求值的函數(shù)中。
fun max(a: Int, b: Int ) = if (a > b) a else b
無(wú)返回值的函數(shù)
類似Java的返回值類型為void的函數(shù)
fun daqi():Unit{
}
可省略Unit類型
fun daqi(){
}
(三)、字符串模板
在 Java 中,當(dāng)需要打印變量描述和其值時(shí),往往如下打?。?/p>
String str = "daqi";
System.out.println("str = " + str);
????Kotlin支持字符串模板,可以在字符串中引用局部變量,但需要在變量名前加上$。表達(dá)式會(huì)進(jìn)行靜態(tài)檢查, 如果$引用一個(gè)不存在的變量,代碼不會(huì)編譯通過(guò)。
val str = "daqi"
printlin("str = $str")
$不僅限于引用于簡(jiǎn)單的變量名稱,還可以引用更復(fù)雜的表達(dá)式。
val daqi = intArrayOf(1,2,3)
println("${daqi[0]}")
在$引用的表達(dá)式內(nèi)(即花括號(hào){}中)可以直接嵌套雙引號(hào)
println("daqi的第一個(gè)元素: ${if(daqi.size > 0) daqi[0] else "null"}")
當(dāng)需要打印$符號(hào)時(shí),可以對(duì)其進(jìn)行轉(zhuǎn)義,或者利用表達(dá)式對(duì)其進(jìn)行打?。?/p>
//打印結(jié)果為:$str
val str = "daqi"
println("\$str")
println(${'$'}daqi)
(四)、類和屬性
在java中定義一個(gè)簡(jiǎn)單類:
public class Person{
private final String name;
public Person(String name){
this.name = name;
}
public String getName(){
return name;
}
}
用Kotlin寫(xiě)的Person類(默認(rèn)public修飾)
class Person(val name:String)
延伸:若想知道Kotlin對(duì)應(yīng)的具體Java實(shí)現(xiàn),可以通過(guò)idea的工具,對(duì)Kotlin文件進(jìn)行反編譯:
????Tools ->Kotlin -> Show Kotlin Bytecode -> 右側(cè)彈出字節(jié)碼框 ->左上角 Decompile按鈕
整體基本和Java類相似,但是該類被定義為final類,即太監(jiān)類,不可被繼承。
setter 和 getter
????在Kotlin中,當(dāng)你聲明屬性的時(shí)候,也就聲明了對(duì)應(yīng)的訪問(wèn)器(即get和set)
????val屬性只有一個(gè)getter,var屬性既有 getter 和 setter。
聲明一個(gè)屬性的完整語(yǔ)法:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
????其中,初始化器,getter 和 setter 都是可選的。如果類型可以從初始化器或者getter 返回值中推斷出來(lái),也可以省略。
訪問(wèn)器的默認(rèn)實(shí)現(xiàn)非常簡(jiǎn)單,即對(duì)對(duì)應(yīng)變量的返回和更改。
當(dāng)需要在值被訪問(wèn)或修改時(shí)提供額外的邏輯,可以通過(guò)自定義getter和setter
自定義getter
在Kotlin中的實(shí)現(xiàn)
class Rectangle(val height:Int,val width:Int){
val isSquare:Boolean
get(){
return height == width
}
}
對(duì)應(yīng)的Java實(shí)現(xiàn):
自定義setter
在Kotlin中實(shí)現(xiàn):
class Rectangle(val height:Int,val width:Int){
var isSquare:Boolean = false
set(value) {
field = value
}
}
在setter方法中,使用特殊的標(biāo)識(shí)符field來(lái)訪問(wèn)支持字段(幕后字段)的值。具體幕后字段后面再說(shuō)。
注意:
????Kolin中,名稱以is開(kāi)頭的變量。轉(zhuǎn)換成對(duì)應(yīng)的Java get 和 set 方法時(shí),getter不會(huì)添加任何的前準(zhǔn),setter名稱中的is會(huì)被替換成set。
class Person(
var isDaqi:Boolean = false
)
該對(duì)象在Java中的訪問(wèn)器為:
(五)、迭代
Kotlin中 while 和 do-while循環(huán),其語(yǔ)法與Java的基本沒(méi)區(qū)別。
而for循環(huán)僅以一種形式存在,和for-each差不多。但用 in 取代了 :
fun iterationArray(args:Array<String>){
for (str in args) {
}
}
????for循環(huán)還可以遍歷區(qū)間。區(qū)間本質(zhì)上是兩個(gè)值之間的間隔。使用..運(yùn)算符表示區(qū)間。
????Kotlin中的區(qū)間是閉合區(qū)間,即結(jié)束值也是區(qū)間的一部分。而區(qū)間默認(rèn)迭代步長(zhǎng)為1。
val oneToTen = 1..10
????可以通過(guò)step關(guān)鍵字修改步長(zhǎng)。遍歷時(shí),i變量其增長(zhǎng)幅度將變?yōu)?,即每次迭代時(shí)都會(huì)自增2。
for(i in 0..100 step 2){
}
如果想要一個(gè)半閉合區(qū)間,即結(jié)束值不屬于區(qū)間的一部分,可以使用until關(guān)鍵字,
val oneToNine = 1 until 10
如果需要一個(gè)倒序的區(qū)間,可以使用downTo關(guān)鍵字
val tenToOne = 10 downTo 1
此時(shí),將從10一直遞減到1進(jìn)行循環(huán)。downTo表示的區(qū)間也是一個(gè)閉合區(qū)間。
如果想實(shí)現(xiàn)普通for循環(huán)一樣,對(duì)數(shù)據(jù)索引進(jìn)行循環(huán),可以使用數(shù)組的indices變量
for (i in args.indices) {
println(args[i])
}
(六)、if表達(dá)式
在Kotlin中,if是一個(gè)表達(dá)式,即它會(huì)返回一個(gè)值。
if的分支為代碼塊時(shí),最后的表達(dá)式將作為該代碼塊的值:
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
(七)、when表達(dá)式
Kotlin中的when,對(duì)應(yīng)的是Java中的switch,但比起更加強(qiáng)大。
????when 將它的參數(shù)與所有的分支條件按順序比較,直到某個(gè)分支滿足條件,執(zhí)行其分支對(duì)應(yīng)的代碼。當(dāng)多分支需要用相同的方式處理時(shí),可以把其放在一起,用逗號(hào)分隔。
????當(dāng)when作為表達(dá)式使用時(shí),必須有 else 分支, 除非編譯器檢測(cè)出所有的可能情況都已經(jīng)被覆蓋。(例如枚舉類)
when (x) {
0, 1 -> print("x == 0 or x == 1")
2 -> print("x == 2")
else -> print("otherwise")
}
有沒(méi)有發(fā)現(xiàn),沒(méi)有了那繁瑣的case和break!!
????when的參數(shù)可有可無(wú),并且無(wú)限制類型!而且可以用任意表達(dá)式作為分支條件。(switch要求必須使用常量作為分支條件~)
????when作為表達(dá)式函數(shù)的的函數(shù)體,直接使用函數(shù)的參數(shù),自身并無(wú)設(shè)置參數(shù)。并在條件語(yǔ)句中使用in或者!in 判斷一個(gè)變量是否在區(qū)間或者集合中。
fun daqi(num:Int,intArray: IntArray) = when{
num in 1..10 -> "1~10"
num !in intArray -> "no in Array"
else -> "other"
}
when 也可以用來(lái)取代 if-else if鏈.
fun daqi(num:Int) = when{
num < 0 -> "小于0"
num >0 && num < 10 -> "1..10"
else -> "other"
}
????is可以檢查一個(gè)變量是否是某種類型,再配合智能轉(zhuǎn)換(檢查過(guò)某個(gè)變量的類型后,不再需要再轉(zhuǎn)換它,直接可以把它當(dāng)作檢查過(guò)的類型使用)可以很方便的對(duì)進(jìn)行類型安全操作。
//Any類似于Java的Object
fun daqi(num:Any) = when(num){
is String -> {
//此時(shí)num已經(jīng)為String類型
println("該變量類型為String,獲取其長(zhǎng)度")
println(num.length)
}
is Int -> {
//此時(shí)num已經(jīng)為Int類型
println("該變量類型為Int,將其與10進(jìn)行對(duì)比")
num.rangeTo(10)
}
else -> "other"
}
(八)、Kotlin中的異常
????Kotlin的異常處理與Java類似。當(dāng)拋出異常時(shí),不需要使用new關(guān)鍵字創(chuàng)建異常實(shí)例。
var daqi:String? = null
if (daqi == null){
throw NullPointerException("daqi is null")
}
????try也可以作為表達(dá)式,將其賦值給變量。當(dāng)代碼執(zhí)行正常,則try代碼塊中最后一個(gè)表達(dá)式作為結(jié)果,如果捕獲到異常,對(duì)應(yīng)catch代碼塊中最后一個(gè)表達(dá)式作為結(jié)果。finally 塊中的內(nèi)容不會(huì)影響表達(dá)式的結(jié)果。
fun readNumber(reader:BufferedReader):Int? = try{
Integer.parseInt(reader.readLine())
}catch(e:NumberFormatException){
null
}catch (e:NullPointerException){
null
}finally {
reader.close()
}
可以有0到多個(gè) catch 塊。finally 塊可以省略。 但是 catch 與 finally 塊至少應(yīng)該存在一個(gè)。
(九)、枚舉
????kotlin中用兩個(gè)關(guān)鍵字enum和class聲明枚舉類。enum是一個(gè)軟關(guān)鍵字,只有當(dāng)它出現(xiàn)在class前面時(shí)才有特殊的意義。
聲明普通枚舉類:
enum class Color{
RED,BLUE
}
聲明帶屬性的枚舉類:
enum class Color(val r:Int,val g:Int,val b:Int){
RED(255,0,0),BLUE(0,0,255);
fun rgb = (r * 256 + g) * 256 + b
}
如果在枚舉類型中定義任何方法,需要使用分號(hào);把枚舉常量列表和方法定義分開(kāi)。
參考文獻(xiàn):
- 《Kotlin實(shí)戰(zhàn)》
- Kotlin官網(wǎng)
android Kotlin系列:
Kotlin知識(shí)歸納(一) —— 基礎(chǔ)語(yǔ)法
Kotlin知識(shí)歸納(二) —— 讓函數(shù)更好調(diào)用
Kotlin知識(shí)歸納(三) —— 頂層成員與擴(kuò)展
Kotlin知識(shí)歸納(六) —— 類型系統(tǒng)