Kotlin核心編程 第一章認(rèn)識Kotlin、第二章基礎(chǔ)語法

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

第一章、認(rèn)識Kotlin

1.3Kotlin---改良的Java

1在很大程度上實(shí)現(xiàn)了類型推導(dǎo),而java在se10才支持了局部變量的推導(dǎo);
2放棄了static關(guān)鍵字,但又引入了object,可以直接用它來聲明一個單例。
3引入了java中沒有的特殊類,比如Data Class(數(shù)據(jù)類)、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 上流行的原因之一。

第二章、基礎(chǔ)語法

2.1不一樣的類型聲明:

類型名放變量名后面

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

增強(qiáng)的類型推導(dǎo):編譯器可以在不顯示聲明類型的情況下,自動推導(dǎo)出他所需要的類型。

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

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

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

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

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

*可以暫時把Unit當(dāng)做java中的void。Unit是一個類型,void只是一個關(guān)鍵字。

kotlin進(jìn)一步增強(qiáng)了函數(shù)的語法,可以把{}去掉,用等號定義一個函數(shù)。

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

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

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的使用規(guī)則

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

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

更加直觀的例子

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

2.3高階函數(shù)和Lambda

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

接下來用一個例子來說明:有一個國家數(shù)據(jù)庫,設(shè)計了一個CountryApp對國家數(shù)據(jù)進(jìn)行操作,現(xiàn)在要獲取所有的歐洲國家

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
    }
}

后來要找其他洲,改進(jìn)了上述方法,加了一個參數(shù)

    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
    }

現(xiàn)在要增加人口的條件,又增加了一個參數(shù),如下:

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
    }

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

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

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

(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,需要一個方法引用表達(dá)式通過::實(shí)現(xiàn)對于某個類的方法進(jìn)行引用。

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

上面仍不算一個好的方案,每次都要在類中專門寫一個新增的篩選方法。用匿名函數(shù)進(jìn)行進(jìn)一步優(yōu)化。

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

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

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

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

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

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

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

類型View被稱為接收者類型,this對應(yīng)的是這個類型所創(chuàng)建的接收者對象,可以被省略。

2.4面向表達(dá)式編程

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

在下述4種特殊情況時,finally塊都不會被執(zhí)行:
1)在finally語句塊中發(fā)生了異常。
2)在前面的代碼中用了System.exit()退出程序。
3)程序所在的線程死亡。
4)關(guān)閉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,結(jié)果會怎樣

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的時候會把返回值壓入棧,并把返回值賦值給棧中的局部變量, 最后把棧頂?shù)淖兞恐底鳛楹瘮?shù)返回值。所以在 finally中的返回值就會覆蓋 try/catch中的返回值,如果 finally中不執(zhí)行 return語句,在 finally中修改返回變量的值,不會影響返回結(jié)果。

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

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

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循環(huán)

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

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

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

downTo實(shí)現(xiàn)倒序

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

util實(shí)現(xiàn)半開區(qū)間

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

用in來檢查成員關(guān)系

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

通過withIndex提供一個鍵值元組

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

in、step、downTo、until是通過中綴表達(dá)式實(shí)現(xiàn)的

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
用三個引號定義的字符創(chuàng),最終的打印格式和在代碼里的格式一致,而且不會解釋轉(zhuǎn)化轉(zhuǎn)義字符

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

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

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

字符串判等
==判斷內(nèi)容是否相等
===判斷引用是否相等

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容