---
# System prepended metadata

title: Kotlin學習筆記

---

# 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()


