Kotlin核心編程 第一章認識Kotlin、第二章基礎語法

本文是Kotlin核心編程(2021年6月第一版第5次印刷)的讀書筆記。
感覺適合有一定了解java的Kotlin初學者,內容講了Kotlin通用的使用場景、方法,原理性內容不是很難。
這里會根據書中順序,把個人感覺比較重要的內容做下記錄。

第一章、認識Kotlin

1.3Kotlin---改良的Java

1在很大程度上實現了類型推導,而java在se10才支持了局部變量的推導;
2放棄了static關鍵字,但又引入了object,可以直接用它來聲明一個單例。
3引入了java中沒有的特殊類,比如Data Class(數據類)、Sealed Classes(密封類)。
4新增了java中沒有的語法糖(Smart Casts)。

//java寫法:
if(parentView instanceOf  ViewGroup){
        ((ViewGroup)parentView).addView(childView)
}
//kotlin寫法:
if(parentView is ViewGroup){
        parentView.addView(childView)
}

兼容了java,Kotlin可以與java 6一起工作,這也是在Android 上流行的原因之一。

第二章、基礎語法

2.1不一樣的類型聲明:

類型名放變量名后面

//String a = "I am Kotlin";
val a :String = "I am Kotlin"

增強的類型推導:編譯器可以在不顯示聲明類型的情況下,自動推導出他所需要的類型。

val string = "I am Kotlin"  //java.lang.String
val int = 11234  //int
val long = 1234L  //long
...

聲明函數返回值類型,類型信息放在函數名后面

fun sum(x:Int,y:Int):Int{return x+y}

如果沒有聲明返回值類型,函數默認被當做返回Unit類型,然而實際上返回的是Int,所以編譯器會報錯。這種情況下必須顯示聲明返回值類型。

fun sum(x:Int,y:Int){return x+y}//!
Type mismatch: inferred type is Int but Unit was expected

*可以暫時把Unit當做java中的void。Unit是一個類型,void只是一個關鍵字。

kotlin進一步增強了函數的語法,可以把{}去掉,用等號定義一個函數。

fun sum(x:Int,y:Int)=x+y

這種用單行表達式與等號的語法來定義函數,叫做表達式函數體,作為區分,普通的函數聲明則可以叫做代碼塊函數體。使用表達式函數體我們可以不聲明返回值類型。但是kotlin并不能針對遞歸情況進行全局類型推導。

fun foo(n:Int) = if(n == 0) 1 else n*foo(n-1)//!

Type checking has run into a recursive problem.
 Easiest workaround: specify types of your declarations explicitly

2.2val和var的使用規則

var代表了變量,val聲明的變量具有java中final關鍵字的效果,也就是引用不可變。

val x = intArrayOf(1,2,3)
x = intArrayOf(2,3,4)//!
Val cannot be reassigned
//因為引用不可變,
//所以x不能指向另一個數組,
//但我們可以修改x指向數組的值
x[0] = 2
println(x[0])
2

更加直觀的例子

class Book (var name:String){//var生命的參數name引用可以被改變
    fun printlnName(){
        println(this.name)
    }
}
fun main() {
    val book = Book("Think in java")//book對象的引用不可變
    book.name = "kotlin"
    book.printlnName()  //"kotlin"
}

2.3高階函數和Lambda

我們可以不經像類一樣在頂層直接定義一個函數,也可以在一個函數內部定義一個局部函數,還可以將函數像普通變量一樣傳遞給另一個函數,或在其他函數內部被返回。
高階函數:接收一個或多個過程作為參數;或者把一個過程作為返回結果。

接下來用一個例子來說明:有一個國家數據庫,設計了一個CountryApp對國家數據進行操作,現在要獲取所有的歐洲國家

data class Country(
    val name:String,
    val continient:String,
    val population:Int)

class CountryApp{
    fun filterCountry(countries:List<Country>):List<Country>{
        val res = mutableListOf<Country>()
        for(c in countries){
            if(c.continient == "EU"){
                res.add(c)
            }
        }
        return res
    }
}

后來要找其他洲,改進了上述方法,加了一個參數

    fun filterCountry(countries:List<Country>,continient:String):List<Country>{
        val res = mutableListOf<Country>()
        for(c in countries){
            if(c.continient == continient){
                res.add(c)
            }
        }
        return res
    }

現在要增加人口的條件,又增加了一個參數,如下:

fun filterCountry(countries:List<Country>,continient:String,population:Int):List<Country>{
        val res = mutableListOf<Country>()
        for(c in countries){
            if(c.continient == continient&& c.population>population){
                res.add(c)
            }
        }
        return res
    }

如果更多的篩選條件會作為方法參數不斷累加,業務邏輯也高度耦合。需要把所有的篩選邏輯行為都抽象成一個參數---把篩選邏輯作為一個方法傳入。

class CountryTest{
    fun isBigCountry(country :Country):Boolean{
        return country.continient == "EU" && country.population>10000
    }
}

Kotlin中函數類型格式非常簡單,有以下幾個特點:
通過->來組織參數類型和返回值類型;
用一個括號包裹參數類型;
返回值即使是Unit也必須顯示聲明。
*如果是無參函數類型,參數部分用()代替;多個參數用逗號分隔

(Int)->Unit
()->Unit
(Int,String)->Unit
(errCode:Int,errMsg:String)->Unit
(errCode:Int,errMsg:String?)->Unit
((errCode:Int,errMsg:String?)->Unit)?
(Int)->((Int)->Unit)

最終上面的方法修改為:

fun filterCountry(
        countries:List<Country>,
        test:(Country)->Boolean
    ):List<Country>{
        val res = mutableListOf<Country>()
        for(c in countries){
            if(test(c)){
                res.add(c)
            }
        }
        return res
    }

然后我們需要把isBigCountry方法傳遞給filterCountry,需要一個方法引用表達式通過::實現對于某個類的方法進行引用。

fun main() {
    val countryApp = CountryApp()
    val countryTest = CountryTest()
    val countries = ......
    countryApp.filterCountry(countries,countryTest::isBigCountry)
}

上面仍不算一個好的方案,每次都要在類中專門寫一個新增的篩選方法。用匿名函數進行進一步優化。

countryApp.filterCountry(
    countries,
    fun(country:Country):Boolean{
        return country.continient == "EU"
        && country.population >10000
    }
)

還有另一種Lambda表達式讓代碼更簡潔,可以理解為簡化表達式后的匿名函數

countryApp.filterCountry(
        countries,
        {
            country->
            country.continient == "EU" && country.population >10000
        }
)

Lambda語法:
一個lambda表達式必須通過{}來包裹;
如果lambda聲明了參數部分類型,且返回值類型支持類型推導,那么lambda變量就可以省略函數類型聲明;
如果lambda變量聲明了函數類型,那么lambda的參數部分類型就可以省略。
此外,如果lambda表達式返回的不是Unit,則默認最后一行表達式的值類型就是返回值類型。

lambda里的it是kotlin簡化后的一種語法糖,叫做單個參數的隱式名稱。代表這個lambda接收的單個參數。

擴展函數--kotlin允許我們在不修改已有類的前提下,給他新增方法:

fun View.invisible(){
    this.visibility = View.INVISIBLE
}

類型View被稱為接收者類型,this對應的是這個類型所創建的接收者對象,可以被省略。

2.4面向表達式編程

*kotlin里的try catch finally(經過測試和java一致)

在下述4種特殊情況時,finally塊都不會被執行:
1)在finally語句塊中發生了異常。
2)在前面的代碼中用了System.exit()退出程序。
3)程序所在的線程死亡。
4)關閉CPU。

fun main() {
    println(test())
}

fun test ():Int{
    try{
        println("try ----")
        var i = 1/0
        return 0;
    }catch(e:Exception){
        println("catch ----")
        return 1;
    }finally{
        println("finally ----")
        return 2;
    }
}

//輸出:
try ----
catch ----
finally ----
2
fun main() {
    println(test())
}

fun test ():Int{
    try{
        println("try ----")
        var i = 1/0
        return 0;
    }catch(e:Exception){
        println("catch ----")
        return 1;
    }finally{
        println("finally ----")
        //return 2; 沒有return后
    }
}

//輸出:
try ----
catch ----
finally ----
1

假如我們不在 finally中 return,結果會怎樣

fun main() {
    println(test())
}
fun test ():Int{
    var i = 999;
    try{
        println("try ----")
        var i = 1/0
        return 0;
    }catch(e:Exception){
        println("catch ----")
        i = 100;
        return i;
    }finally{
        println("finally ----")
        i = 200;
        return i;
    }
}

//輸出:
try ----
catch ----
finally ----
200
fun main() {
    println(test())
}
fun test ():Int{
    var i = 999;
    try{
        println("try ----")
        var i = 1/0
        return 0;
    }catch(e:Exception){
        println("catch ----")
        i = 100;
        return i;
    }finally{
        println("finally ----")
        i = 200;
        //return i;沒有return后
    }
}

//輸出:
try ----
catch ----
finally ----
100

在 return的時候會把返回值壓入棧,并把返回值賦值給棧中的局部變量, 最后把棧頂的變量值作為函數返回值。所以在 finally中的返回值就會覆蓋 try/catch中的返回值,如果 finally中不執行 return語句,在 finally中修改返回變量的值,不會影響返回結果。

Kotlin中的“?:”被叫做Elvis運算符,表示一種類型的可空性

Kotlin中的when表達式:
由when開始,{}包含多個分支,每個分支用->連接,不需要switch 和 break,由上到下,依次匹配否則執行else;
最終整個when表達式的返回類型就是所有分支相同的返回類型,或者公共類型。

fun foo(a:Int) = when(a){
    1->1
    2->2
    else ->0
}
或者:
fun foo(a:Int) = when{
    a==1->1
    a==2->2
    else ->0
}

kotlin中的for循環

for (i in 1..10)println(i)
或者
for (i:Int  in 1..10)println(i)

范圍表達式range通過rangeTo函數實現的,通過..操作符與某種類型對象組成。除了整形的基本類型之外,該類型需要實現java.lang.Comparable接口
字符串的大小根據首字母在字母表中的排序比較,相同則從左到右一次比較
step函數來定義迭代步長

for (i in 1..10 step 2)println(i)

downTo實現倒序

for (i in 1..10 downTo 1 step 2)println(i)//通過downTo 而不是10..1
//108642

util實現半開區間

for (i in 1 util10)println(i)
//123456789

用in來檢查成員關系

"a" in listOf("a","b","c")

通過withIndex提供一個鍵值元組

for((index,value) in array.withIndex()){
    println("the element at $index is $value")
}

in、step、downTo、until是通過中綴表達式實現的

2.5字符串的定義和操作

val str = "hello world!"

str.length//12
str.substring(0,5)//hello
str+"hello kotlin!"http://hello world!hello kotlin!
str[0]//h
str.first()//h

"".isEmpty()//t
"".isBlank()//t
用三個引號定義的字符創,最終的打印格式和在代碼里的格式一致,而且不會解釋轉化轉義字符

    val html = """<html>
                        <body>
                            <p>hello</p>
                        </body>
                   </html>  
                """

字符串模板${}提升緊湊型和可讀性

Hi ${name},welcome  to ${lang}

字符串判等
==判斷內容是否相等
===判斷引用是否相等

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容