前序
在19年的Google I/O大會上,Kotlin 成為 Android 開發首選語言。而著名的OkHttp 已經開始用 Kotlin 進行重寫工作。是時候通過寫部落格歸納來鞏固Kotlin基礎知識。
(一)、語法上的變更
- 建立物件不需要new關鍵字
- 語句不需要;結尾,加;也無所謂
- 變數型別後置,即變數名在前,變數型別在後。例如 str:String
- 使用 println() 替代 System.out.println()
(二)、定義變數
Kotlin中使用val關鍵字宣告常量(即變數初始化之後不可再次賦值),var關鍵字宣告變數。
變數定義時可以不顯示指定型別,編譯器會根據其初始化器的型別進行推斷。
//自動推斷出 `Int` 型別
var daqi = 1
//可以顯式地指定變數的型別
val a :String = "daqi"
//如果變數沒有初始化器,需要顯式地指定它的型別
val c: Int
c = 3
複製程式碼
當編譯器能確保val變數只有唯一一條初始化語句會被執行,可以根據條件對它初始化不同的值。
val daqi:String
var isChange = true
if (isChange){
daqi = "1"
}else{
daqi = "2"
}
複製程式碼
val變數的引用自身是不可變的,但是它指向的物件是可變的。
val languages = arrayListOf("Java")
languages.add("Kotlin")
複製程式碼
var 關鍵字允許變數改變自己的值,但不能改變自己的型別。
var daqi = 1
//編譯不通過
daqi = ""
複製程式碼
(三)、定義函式 Kotlin 中使用 fun 關鍵字宣告函式。 完整的函式定義:
修飾符(預設public)+ fun + 方法名 + (引數列表) :返回值 {
函式體
}
複製程式碼
public fun daqi(name:String):String {
}
複製程式碼
表示式函式體
當單個表示式構成完整的函式體時,可以直接去掉 {} 和 return 語句,函式的返回值型別編譯器也會自動推斷。這種函式就是表示式函式。
fun sum(a: Int, b: Int) = a + b
複製程式碼
語句和表示式的區別在於:
- 表示式有值,並且能作為另一個表示式的一部分使用;
- 語句總是包圍著它的程式碼塊中的頂層元素,並且沒有自己的值。
在 Java 中,所有的控制結構都是語句。而在 Kotlin 中,除了迴圈( for, do 和 do/while )以外大多數控制結構都是表示式(例如:if、 when 以及 try 屬於表示式)
表示式函式不光用在些簡單的單行函式中 ,也可以用在對更復雜的單個表示式求值的函式中。
fun max(a: Int, b: Int ) = if (a > b) a else b
複製程式碼
無返回值的函式
類似Java的返回值型別為void的函式
fun daqi():Unit{
}
複製程式碼
可省略Unit型別
fun daqi(){
}
複製程式碼
(三)、字串模板
在 Java 中,當需要列印變數描述和其值時,往往如下列印:
String str = "daqi";
System.out.println("str = " + str);
複製程式碼
Kotlin支援字串模板,可以在字串中引用區域性變數,但需要在變數名前加上$。表示式會進行靜態檢查, 如果$引用一個不存在的變數,程式碼不會編譯通過。
val str = "daqi"
printlin("str = $str")
複製程式碼
$不僅限於引用於簡單的變數名稱,還可以引用更復雜的表示式。
val daqi = intArrayOf(1,2,3)
println("${daqi[0]}")
複製程式碼
在$引用的表示式內(即花括號{}中)可以直接巢狀雙引號
println("daqi的第一個元素: ${if(daqi.size > 0) daqi[0] else "null"}")
複製程式碼
當需要列印$符號時,可以對其進行轉義,或者利用表示式對其進行列印:
//列印結果為:$str
val str = "daqi"
println("\$str")
println(${'$'}daqi)
複製程式碼
(四)、類和屬性
在java中定義一個簡單類:
public class Person{
private final String name;
public Person(String name){
this.name = name;
}
public String getName(){
return name;
}
}
複製程式碼
用Kotlin寫的Person類(預設public修飾)
class Person(val name:String)
複製程式碼
延伸:若想知道Kotlin對應的具體Java實現,可以通過idea的工具,對Kotlin檔案進行反編譯:
Tools ->Kotlin -> Show Kotlin Bytecode -> 右側彈出位元組碼框 ->左上角 Decompile按鈕
整體基本和Java類相似,但是該類被定義為final類,即太監類,不可被繼承。setter 和 getter
在Kotlin中,當你宣告屬性的時候,也就宣告瞭對應的訪問器(即get和set)
val屬性只有一個getter,var屬性既有 getter 和 setter。 宣告一個屬性的完整語法:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
複製程式碼
其中,初始化器,getter 和 setter 都是可選的。如果型別可以從初始化器或者getter 返回值中推斷出來,也可以省略。 訪問器的預設實現非常簡單,即對對應變數的返回和更改。
當需要在值被訪問或修改時提供額外的邏輯,可以通過自定義getter和setter
自定義getter
在Kotlin中的實現
class Rectangle(val height:Int,val width:Int){
val isSquare:Boolean
get(){
return height == width
}
}
複製程式碼
對應的Java實現:
自定義setter
在Kotlin中實現:
class Rectangle(val height:Int,val width:Int){
var isSquare:Boolean = false
set(value) {
field = value
}
}
複製程式碼
在setter方法中,使用特殊的識別符號field來訪問支援欄位(幕後欄位)的值。具體幕後欄位後面再說。
注意:
Kolin中,名稱以is開頭的變數。轉換成對應的Java get 和 set 方法時,getter不會新增任何的前準,setter名稱中的is會被替換成set。
class Person(
var isDaqi:Boolean = false
)
複製程式碼
該物件在Java中的訪問器為:
(五)、迭代
Kotlin中 while 和 do-while迴圈,其語法與Java的基本沒區別。
而for迴圈僅以一種形式存在,和for-each差不多。但用 in 取代了 :
fun iterationArray(args:Array<String>){
for (str in args) {
}
}
複製程式碼
for迴圈還可以遍歷區間。區間本質上是兩個值之間的間隔。使用..運算子表示區間。
Kotlin中的區間是閉合區間,即結束值也是區間的一部分。而區間預設迭代步長為1。
val oneToTen = 1..10
複製程式碼
可以通過step關鍵字修改步長。遍歷時,i變數其增長幅度將變為2,即每次迭代時都會自增2。
for(i in 0..100 step 2){
}
複製程式碼
如果想要一個半閉合區間,即結束值不屬於區間的一部分,可以使用until關鍵字,
val oneToNine = 1 until 10
複製程式碼
如果需要一個倒序的區間,可以使用downTo關鍵字
val tenToOne = 10 downTo 1
複製程式碼
此時,將從10一直遞減到1進行迴圈。downTo表示的區間也是一個閉合區間。
如果想實現普通for迴圈一樣,對資料索引進行迴圈,可以使用陣列的indices變數
for (i in args.indices) {
println(args[i])
}
複製程式碼
(六)、if表示式
在Kotlin中,if是一個表示式,即它會返回一個值。
if的分支為程式碼塊時,最後的表示式將作為該程式碼塊的值:
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
複製程式碼
(七)、when表示式
Kotlin中的when,對應的是Java中的switch,但比起更加強大。
when 將它的引數與所有的分支條件按順序比較,直到某個分支滿足條件,執行其分支對應的程式碼。當多分支需要用相同的方式處理時,可以把其放在一起,用逗號分隔。
當when作為表示式使用時,必須有 else 分支, 除非編譯器檢測出所有的可能情況都已經被覆蓋。(例如列舉類)
when (x) {
0, 1 -> print("x == 0 or x == 1")
2 -> print("x == 2")
else -> print("otherwise")
}
複製程式碼
有沒有發現,沒有了那繁瑣的case和break!!
when的引數可有可無,並且無限制型別!而且可以用任意表示式作為分支條件。(switch要求必須使用常量作為分支條件~)
when作為表示式函式的的函式體,直接使用函式的引數,自身並無設定引數。並在條件語句中使用in或者!in 判斷一個變數是否在區間或者集合中。
fun daqi(num:Int,intArray: IntArray) = when{
num in 1..10 -> "1~10"
num !in intArray -> "no in Array"
else -> "other"
}
複製程式碼
when 也可以用來取代 if-else if鏈.
fun daqi(num:Int) = when{
num < 0 -> "小於0"
num >0 && num < 10 -> "1..10"
else -> "other"
}
複製程式碼
is可以檢查一個變數是否是某種型別,再配合智慧轉換(檢查過某個變數的型別後,不再需要再轉換它,直接可以把它當作檢查過的型別使用)可以很方便的對進行型別安全操作。
//Any類似於Java的Object
fun daqi(num:Any) = when(num){
is String -> {
//此時num已經為String型別
println("該變數型別為String,獲取其長度")
println(num.length)
}
is Int -> {
//此時num已經為Int型別
println("該變數型別為Int,將其與10進行對比")
num.rangeTo(10)
}
else -> "other"
}
複製程式碼
(八)、Kotlin中的異常
Kotlin的異常處理與Java類似。當丟擲異常時,不需要使用new關鍵字建立異常例項。
var daqi:String? = null
if (daqi == null){
throw NullPointerException("daqi is null")
}
複製程式碼
try也可以作為表示式,將其賦值給變數。當程式碼執行正常,則try程式碼塊中最後一個表示式作為結果,如果捕獲到異常,對應catch程式碼塊中最後一個表示式作為結果。finally 塊中的內容不會影響表示式的結果。
fun readNumber(reader:BufferedReader):Int? = try{
Integer.parseInt(reader.readLine())
}catch(e:NumberFormatException){
null
}catch (e:NullPointerException){
null
}finally {
reader.close()
}
複製程式碼
可以有0到多個 catch 塊。finally 塊可以省略。 但是 catch 與 finally 塊至少應該存在一個。
(九)、列舉
kotlin中用兩個關鍵字enum和class宣告列舉類。enum是一個軟關鍵字,只有當它出現在class前面時才有特殊的意義。
宣告普通列舉類:
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
}
複製程式碼
如果在列舉型別中定義任何方法,需要使用分號;把列舉常量列表和方法定義分開。
參考資料:
- 《Kotlin實戰》
- Kotlin官網