Kotlin知識歸納(一) —— 基礎語法

大棋發表於2019-05-28

前序

        在19年的Google I/O大會上,Kotlin 成為 Android 開發首選語言。而著名的OkHttp 已經開始用 Kotlin 進行重寫工作。是時候通過寫部落格歸納來鞏固Kotlin基礎知識。

(一)、語法上的變更

  • 建立物件不需要new關鍵字
  • 語句不需要;結尾,加;也無所謂
  • 變數型別後置,即變數名在前,變數型別在後。例如 str:String
  • 使用 println() 替代 System.out.println()

(二)、定義變數

        Kotlin中使用val關鍵字宣告常量(即變數初始化之後不可再次賦值),var關鍵字宣告變數。

變數定義時可以不顯示指定型別,編譯器會根據其初始化器的型別進行推斷。

//自動推斷出 `Int` 型別
var daqi = 1
//可以顯式地指定變數的型別
val a :String = "daqi"
//如果變數沒有初始化器,需要顯式地指定它的型別
val c: Int
c = 3  
複製程式碼

當編譯器能確保val變數只有唯一一條初始化語句會被執行,可以根據條件對它初始化不同的值。

val daqi:String
var isChange = true
if (isChange){
   daqi = "1"
}else{
   daqi = "2"
}
複製程式碼

val變數的引用自身是不可變的,但是它指向的物件是可變的。

val languages = arrayListOf("Java")
languages.add("Kotlin")
複製程式碼

var 關鍵字允許變數改變自己的值,但不能改變自己的型別。

var daqi = 1
//編譯不通過
daqi = ""
複製程式碼

(三)、定義函式 Kotlin 中使用 fun 關鍵字宣告函式。 完整的函式定義:

修飾符(預設public)+ fun + 方法名 + (引數列表) :返回值 {
        函式體
}
複製程式碼

Kotlin知識歸納(一) —— 基礎語法

public fun daqi(name:String):String {
}
複製程式碼

表示式函式體

        當單個表示式構成完整的函式體時,可以直接去掉 {} 和 return 語句,函式的返回值型別編譯器也會自動推斷。這種函式就是表示式函式。

fun sum(a: Int, b: Int) = a + b
複製程式碼

語句和表示式的區別在於:

  • 表示式有值,並且能作為另一個表示式的一部分使用;
  • 語句總是包圍著它的程式碼塊中的頂層元素,並且沒有自己的值。

        在 Java 中,所有的控制結構都是語句。而在 Kotlin 中,除了迴圈( for, do 和 do/while )以外大多數控制結構都是表示式(例如:if、 when 以及 try 屬於表示式)

         表示式函式不光用在些簡單的單行函式中 ,也可以用在對更復雜的單個表示式求值的函式中。

fun max(a: Int, b: Int ) = if (a > b) a else b
複製程式碼

無返回值的函式

類似Java的返回值型別為void的函式

fun daqi():Unit{
}
複製程式碼

可省略Unit型別

fun daqi(){
}
複製程式碼

(三)、字串模板

在 Java 中,當需要列印變數描述和其值時,往往如下列印:

String str = "daqi";
System.out.println("str = " + str);
複製程式碼

    Kotlin支援字串模板,可以在字串中引用區域性變數,但需要在變數名前加上$。表示式會進行靜態檢查, 如果$引用一個不存在的變數,程式碼不會編譯通過。

val str = "daqi"
printlin("str = $str")
複製程式碼

$不僅限於引用於簡單的變數名稱,還可以引用更復雜的表示式。

val daqi = intArrayOf(1,2,3)
println("${daqi[0]}")
複製程式碼

在$引用的表示式內(即花括號{}中)可以直接巢狀雙引號

println("daqi的第一個元素: ${if(daqi.size > 0) daqi[0] else "null"}")
複製程式碼

當需要列印$符號時,可以對其進行轉義,或者利用表示式對其進行列印:

//列印結果為:$str
val str = "daqi"
println("\$str")
println(${'$'}daqi)
複製程式碼

(四)、類和屬性

在java中定義一個簡單類:

public class Person{
    private final String name;
    
    public Person(String name){
        this.name = name;
    }
    
    public String getName(){
    	return name;
    }
}
複製程式碼

用Kotlin寫的Person類(預設public修飾)

class Person(val name:String)
複製程式碼

延伸:若想知道Kotlin對應的具體Java實現,可以通過idea的工具,對Kotlin檔案進行反編譯:

    Tools ->Kotlin -> Show Kotlin Bytecode -> 右側彈出位元組碼框 ->左上角 Decompile按鈕

Kotlin知識歸納(一) —— 基礎語法

Kotlin知識歸納(一) —— 基礎語法
整體基本和Java類相似,但是該類被定義為final類,即太監類,不可被繼承。

setter 和 getter

    在Kotlin中,當你宣告屬性的時候,也就宣告瞭對應的訪問器(即get和set)

    val屬性只有一個getter,var屬性既有 getter 和 setter。 宣告一個屬性的完整語法:

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]
複製程式碼

    其中,初始化器,getter 和 setter 都是可選的。如果型別可以從初始化器或者getter 返回值中推斷出來,也可以省略。 訪問器的預設實現非常簡單,即對對應變數的返回和更改。

當需要在值被訪問或修改時提供額外的邏輯,可以通過自定義getter和setter

自定義getter

在Kotlin中的實現

class Rectangle(val height:Int,val width:Int){
    val isSquare:Boolean
        get(){
        	return height == width
    	}
}
複製程式碼

對應的Java實現:

Kotlin知識歸納(一) —— 基礎語法

自定義setter

在Kotlin中實現:

class Rectangle(val height:Int,val width:Int){
    var isSquare:Boolean = false
        set(value) {
            field = value
        }
}
複製程式碼

在setter方法中,使用特殊的識別符號field來訪問支援欄位(幕後欄位)的值。具體幕後欄位後面再說。

注意:

    Kolin中,名稱以is開頭的變數。轉換成對應的Java get 和 set 方法時,getter不會新增任何的前準,setter名稱中的is會被替換成set。

class Person(
    var isDaqi:Boolean = false
)
複製程式碼

該物件在Java中的訪問器為:

Kotlin知識歸納(一) —— 基礎語法

(五)、迭代

Kotlin中 while 和 do-while迴圈,其語法與Java的基本沒區別。

而for迴圈僅以一種形式存在,和for-each差不多。但用 in 取代了 :

fun iterationArray(args:Array<String>){
    for (str in args) {

    }
}
複製程式碼

    for迴圈還可以遍歷區間。區間本質上是兩個值之間的間隔。使用..運算子表示區間。

    Kotlin中的區間是閉合區間,即結束值也是區間的一部分。而區間預設迭代步長為1。

val oneToTen = 1..10
複製程式碼

    可以通過step關鍵字修改步長。遍歷時,i變數其增長幅度將變為2,即每次迭代時都會自增2。

for(i in 0..100 step 2){
}
複製程式碼

如果想要一個半閉合區間,即結束值不屬於區間的一部分,可以使用until關鍵字,

val oneToNine = 1 until 10
複製程式碼

如果需要一個倒序的區間,可以使用downTo關鍵字

val tenToOne = 10 downTo 1
複製程式碼

此時,將從10一直遞減到1進行迴圈。downTo表示的區間也是一個閉合區間。

如果想實現普通for迴圈一樣,對資料索引進行迴圈,可以使用陣列的indices變數

for (i in args.indices) {
    println(args[i])
}
複製程式碼

(六)、if表示式

在Kotlin中,if是一個表示式,即它會返回一個值。

if的分支為程式碼塊時,最後的表示式將作為該程式碼塊的值:

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}
複製程式碼

(七)、when表示式

Kotlin中的when,對應的是Java中的switch,但比起更加強大。

    when 將它的引數與所有的分支條件按順序比較,直到某個分支滿足條件,執行其分支對應的程式碼。當多分支需要用相同的方式處理時,可以把其放在一起,用逗號分隔。

    當when作為表示式使用時,必須有 else 分支, 除非編譯器檢測出所有的可能情況都已經被覆蓋。(例如列舉類)

when (x) {
    0, 1 -> print("x == 0 or x == 1")
       2 -> print("x == 2")
    else -> print("otherwise")
}
複製程式碼

有沒有發現,沒有了那繁瑣的case和break!!

    when的引數可有可無,並且無限制型別!而且可以用任意表示式作為分支條件。(switch要求必須使用常量作為分支條件~)

    when作為表示式函式的的函式體,直接使用函式的引數,自身並無設定引數。並在條件語句中使用in或者!in 判斷一個變數是否在區間或者集合中。

fun daqi(num:Int,intArray: IntArray) = when{
    num in 1..10 -> "1~10"
    num !in intArray -> "no in Array"
    else -> "other"
}
複製程式碼

when 也可以用來取代 if-else if鏈.

fun daqi(num:Int) = when{
    num < 0 -> "小於0"
    num >0 && num < 10 -> "1..10"
    else -> "other"
}
複製程式碼

    is可以檢查一個變數是否是某種型別,再配合智慧轉換(檢查過某個變數的型別後,不再需要再轉換它,直接可以把它當作檢查過的型別使用)可以很方便的對進行型別安全操作。

//Any類似於Java的Object
fun daqi(num:Any) = when(num){
    is String -> {
        //此時num已經為String型別
        println("該變數型別為String,獲取其長度")
        println(num.length)
    }
    is Int -> {
        //此時num已經為Int型別
        println("該變數型別為Int,將其與10進行對比")
            num.rangeTo(10)
    }
    else -> "other"
}
複製程式碼

(八)、Kotlin中的異常

    Kotlin的異常處理與Java類似。當丟擲異常時,不需要使用new關鍵字建立異常例項。

var daqi:String? = null
if (daqi == null){
    throw NullPointerException("daqi is null")
}
複製程式碼

    try也可以作為表示式,將其賦值給變數。當程式碼執行正常,則try程式碼塊中最後一個表示式作為結果,如果捕獲到異常,對應catch程式碼塊中最後一個表示式作為結果。finally 塊中的內容不會影響表示式的結果。

fun readNumber(reader:BufferedReader):Int? = try{
    Integer.parseInt(reader.readLine())
}catch(e:NumberFormatException){
    null
}catch (e:NullPointerException){
    null
}finally {
    reader.close()
}
複製程式碼

可以有0到多個 catch 塊。finally 塊可以省略。 但是 catch 與 finally 塊至少應該存在一個。

(九)、列舉

    kotlin中用兩個關鍵字enum和class宣告列舉類。enum是一個軟關鍵字,只有當它出現在class前面時才有特殊的意義。

宣告普通列舉類:

enum class Color{
    RED,BLUE
}
複製程式碼

宣告帶屬性的列舉類:

enum class Color(val r:Int,val g:Int,val b:Int){
    RED(255,0,0),BLUE(0,0,255);
    fun rgb = (r * 256 + g) * 256 + b
}
複製程式碼

如果在列舉型別中定義任何方法,需要使用分號;把列舉常量列表和方法定義分開。

參考資料:

android Kotlin系列:

Kotlin知識歸納(一) —— 基礎語法

Kotlin知識歸納(二) —— 讓函式更好呼叫

Kotlin知識歸納(三) —— 頂層成員與擴充套件

Kotlin知識歸納(四) —— 介面和類

Kotlin知識歸納(五) —— Lambda

Kotlin知識歸納(六) —— 型別系統

Kotlin知識歸納(七) —— 集合

Kotlin知識歸納(八) —— 序列

Kotlin知識歸納(九) —— 約定

Kotlin知識歸納(十) —— 委託

Kotlin知識歸納(十一) —— 高階函式

Kotlin知識歸納(十二) —— 泛型

Kotlin知識歸納(十三) —— 註解

Kotlin知識歸納(十四) —— 反射

相關文章