# 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

## 匿名函數與函數類型:
**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()