# Kotlin學習筆記 ## 變數,常數,類: 1. 可變性/不可變性 **val**:宣告即初始化,不可複寫,不能有 setter,可便於追蹤 **var**:會自動判變型別,有 setter **const**:定義常數,通常使用於恆遠不變的數值 **lateinit var**:針對var的延遲宣告 **Companion Object**:宣告靜態(static)變數/function **lazy**:針對val的延遲宣告 示範: **Companion Object**: ``` class Com{ companion object { fun staticFunc(){} val STATICOBJ = Date()} } ``` **val**: const val name = "Kotlin" val subName:Sting = "" **var**: var apple:Int =2 private var textView: TextView? = null //允許null,延遲初始化 **lateinit var**: private lateinit var textView: TextView //延遲初始化,不允許為null **lazy**: private val textView: TextView by lazy { println("this is lazy function block") findViewById(R.id.textView) as TextView } //因為宣告後要先取得物件才能賦值,所以不能在宣告時立刻賦值 2. 修飾字 **private**:意味著只在這個類內部(包含其所有成員)可見 **protected**:和 private一樣 + 在子類中可見。 **internal**:訪問僅限於當前程序集 **public**:如果沒有顯式指定修飾符的話,默認可見性是 public。 internal修飾的方法或著屬性,只要是在同一程序集的中的其他類都可以訪問,如果二者不再同一命名空間,只要使用using引用上相應的命名空間即可; 而程序集是什麼呢?被編譯到同一個dll或exe中的程序就是處於同一程序集。 3. 類 **創造**: ``` class Runoob { // 類名為 Runoob // 大括號內是類體構成 } ``` **建構**: ``` val site = Runoob() // Kotlin 中沒有 new 關鍵字 ``` **主構造函數**: ``` class Person constructor(firstName: String) { } ``` 主構造函數不能包含任何代碼。 初始化代碼可以放在初始化程序塊中,前綴爲init關鍵字: ``` class Customer(name: String) { init { logger.info("Customer initialized with value ${name}") } } ``` **多個輔助構造函數**: 類還可以聲明輔助構造函數,它們以constructor關鍵字作爲前綴: ``` class Person { constructor(parent: Person) { parent.children.add(this) } } ``` ## 函数: 函數作用域: 1. Kotlin 支持局部函數,即一個函數在另一個函數內部 2. 函數可以有泛型參數,通過在函數名前使用尖括號指定。 ``` fun <T> singletonList(item: T): List<T> { // …… } ``` **宣告函數**:如果函數沒有返回任何值,則返回類型爲Unit。 ``` 普通 fun functionName(){ // 函數體 } ``` ``` 參數 fun functionName(number:Int,name:String){ // 函數體 } ``` ``` 參數 fun functionName(number,name){ // 函數體 } ``` ``` 回傳Int fun sum(number1: Int, number2:Int): Int{ val result = number1+number2 return result } ``` **遞回函數**:有條件地去限制呼叫自己 ``` var count = 0 fun rec(){ count++; if(count<=5){ println("count => "+count); rec(); } } fun main(args: Array<String>) { rec(); } ``` **中綴表示法**: 當 他們是成員函數或擴展函數 他們只有一個參數 他們用 infix 關鍵字標註 ``` // 給 Int 這個類別定義擴展函數 infix fun Int.shl(x: Int): Int { …… } // 用中綴表示法調用擴展函數 1 shl 2 // 等同於這樣 1.shl(2) ``` **可變數量的參數(Varargs)**:好像方便,可傳多個同類別 ``` 函數的參數(通常是最後一個)可以用 vararg 修飾符標記: fun <T> asList(vararg ts: T): List<T> { val result = ArrayList<T>() for (t in ts) // ts is an Array result.add(t) return result } ``` ``` 允許將可變數量的參數傳遞給函數: val list = asList(1, 2, 3) ``` **默認參數**:即使傳入空值,也會自動使用默認值 ``` fun run(num:Int= 5, latter: Char ='x'){ print("parameter in function definition $num and $latter") } ``` **注意**:run(latter='a')->可指定參數名稱進行傳遞,才不容易報錯 ## 拓展函数: **let**: **run**: **also**: **apply**: **with**: 參考:https://blog.csdn.net/Jokey_wz/article/details/92846392 ![](https://i.imgur.com/kyZSimI.png) ## 匿名函數與函數類型: **Lambda函數**:不須宣告,只需在使用時定義。 Lambda是用花括號{}定義的,它將變量作爲參數(如果有的話)和函數體。 函數體在變量(如果有)之後寫入,後跟 -> 運算符 ``` lambda的語法 { variable -> body_of_function} ``` ``` 創建一個lambda表達式{s: Int -> println(s) }, 其返回類型爲Unit。 lambda函數作爲*高級函數*addNumber(5,10,myLambda)中的參數。 fun main(args: Array<String>){ val myLambda: (Int) -> Unit= {s: Int -> println(s) } //lambda function addNumber(5,10,myLambda) } fun addNumber(a: Int, b: Int, mylambda: (Int) -> Unit ){ //high level function lambda as parameter val add = a + b mylambda(add) // println(add) } ``` **高級函數**:可以接受函數作爲參數,也可以返回函數 ``` myFun是高階函數, fun myFun(org: String,portal: String, fn: (String,String) -> String): Unit { val result = fn(org,portal) println(result) } fn是lambda函數,return一個String, fun main(args: Array<String>){ val fn:(String,String)->String={org,portal->"$org develop $portal"} myFun("yiibai.org","yiibai.com",fn) } ``` ``` body 擁有函數類型:() -> T, 所以它應該是一個不帶參數並且返回 T 類型值的函數。 fun <T> lock(lock: Lock, body: () -> T): T { lock.lock() try { return body() } finally { lock.unlock() } } ``` **內聯函數**:如果一個函數很短,無循環、遞歸等複雜操作,且該函數經常被調用(指運行的次數多);那麼為了減少調用的開銷(保存、恢復現場),將函數聲明為內聯函數,可以讓編譯器在編譯階段將其在調用位置進行展開。 ``` fun main(args: Array<String>) { inlineFunction({ println("調用內聯函數")}) } inline fun inlineFunction(myFun: () -> Unit ) { myFun() print("內聯函數內的代碼") } ``` ## null安全與異常: Kotlin null安全性是一種消除代碼中空引用風險的過程。 如果Kotlin編譯器發現任何null參數而仍然執行null引用相關語句,則會立即拋出NullPointerException。 通常所有類型不可為空,要創建保存null值的字符串,必須通過放置一個?來明確定義它們。 例如,在String後面使用:String? **可空類型** 通過放置一個?來聲明可空類型? 在String後面: ``` var str1: String? = "hello" str1 = null // ok ``` 當嘗試在沒有安全轉換的情況下訪問可空類型的String時,它將生成編譯錯誤。 ``` var string: String? = "Hello!" print(string.length) // Compile error ``` ``` 要解決上述表達式,使用安全轉換爲: fun main(args: Array<String>){ var string: String? = "Hello!" if(string != null) { // smart cast print(string.length) // It works now! } } ``` **非可空類型** 通過放置一個?來聲明可空類型? 在String後面: ``` var str1: String = "hello" str1 = null // !!!error ``` **Elvis**:用於檢查值的空安全性 ``` 一般: var len1: Int = if (str != null) str.length else -1 var len2: Int = if (str2 != null) str2.length else -1 ``` ``` Elvis: var len1: Int = str ?.length ?: -1 var len2: Int = str2 ?.length ?: -1 ``` ## 類型轉換操作符: **is**:當使用is或!is可以檢查變數類型時,回傳bool ``` 使用is來智能轉換 fun main(args: Array<String>){ val obj: Any = "變量obj自動轉換爲此範圍內的String" if(obj is String) { // No Explicit Casting needed. println("字符串的長度是:${obj.length}") } } ``` **as?**:as?安全地轉換成一種類型。 如果無法進行轉換,則返回null,而不是拋出ClassCastException異常。 不安全: ``` 可以爲空的字符串(String?)不能轉換爲非null字符串(String),這會引發異常。 un main(args: Array<String>){ val obj: Any? = null val str: String = obj as String println(str) } ``` 安全: ``` as關鍵字 fun main(args: Array<String>){ val obj: String? = "String unsafe cast" val str: String? = obj as String? // Works println(str) } ``` ``` as?關鍵字 fun main(args: Array<String>){ val location: Any = "Kotlin" val safeString: String? = location as? String val safeInt: Int? = location as? Int println(safeString) println(safeInt) } ``` ## 字串: String類表示char ``` val ch = charArrayOf('h', 'e', 'l', 'l', 'o') val st = String(ch) ``` **String屬性:** **length: Int**:它返回字符串序列的長度。 **indices:** 它返回當前char序列中有效字符索引的範圍。 **lastIndex: Int**:它返回char序列中最後一個字符的索引。 **訪問**:以index方式索取,通常會用到迴圈 ``` val str ="Hello, yiibai" println(str[0]) //prints H ``` **常用針對字串的函數** 參考:https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/662774/ 切割,擷取:subString(),split()->使用正則表達式分割,使用字符或字符串分割 替換:replace()