1. 程式人生 > >《Kotlin實戰》學習筆記之第二章:Kotlin基礎

《Kotlin實戰》學習筆記之第二章:Kotlin基礎

一、基本要素:函式和變數

1、Hello,world
fun main(args: Array<Stirng>) {
    println("Hello, world!")
}
  • 關鍵字fun宣告函式
  • 陣列就是類。Kotlin沒有宣告陣列型別的特殊語法
  • 可以省略每行程式碼結尾的分號
2、函式
//:後跟的是函式或引數的型別

fun max(a: Int, b: Int): Int {
    return if (a > b) a else b
}
  • 語句與表示式

    • 表示式有值,並能作為另一個表示式的一部分使用。語句則沒有值。

    • Java中賦值操作是表示式,Kotlin中是語句

    • Kotlin中,除了迴圈(for、do和do/while)以外的大多數控制結構都是表示式

  • 函式體

    • (1)表示式函式體
      • 可以去掉花括號
      • 可以去掉return語句
      fun max(a: Int, b: Int): Int = if (a > b) a else b
      
    • (2)程式碼塊體函式
      • 有返回值且必須顯式表示
3、變數
  • 書寫方式

    val answer: Int = 42
    
    • val/var關鍵字開始
    • 變數名: 變數型別(可省略)
  • 變數種類

    • (1)val(來自value):不可變引用
      • 等價於java中的final
      • 該關鍵字宣告的變數不可在初始化之後再次賦值
      • val無需在宣告時定義
        • 儘管val引用自身是不可變的,但是它指向的物件可能是可變的
        val languages = arrayListOf("Java")
        languages.add("Kotlin")   //val指向的物件可變
        
    • (2)var(來自variable):可變引用
      • 對應java中的普通變數
      • var關鍵字允許變數在初始化後改變自己的值,但是它的型別是不能再改變的,除非手動的進行型別轉換
        • 因為編譯器只會根據初始化器來推斷變數的型別
        var answer = 42
        answer = "no answer"  //錯誤!初始化後不可改變型別
        
  • “應該儘可能的使用val關鍵字來宣告所有的kotlin變數”

    • 更接近函數語言程式設計的風格
4、字串模版
  • 用於引用區域性變數,使用$${}
    • (1)直接引用字串
      val name = "wang"
      
      println("Hello, $name !")
      等價於
      System.out.println("Hello, wang !")
      
    • (2)使用${}語法插入陣列元素
      println("Hello,${args[0]}")
      
    • (3)使用${表示式}
      println("Hello, ${if (args.size > 0) args[0] else "someone"}!")
      

二、類和屬性

1、值物件
class Person(val name: String)
  • 這種只有資料沒有其他程式碼的類被稱作值物件
  • 等價於javabean
  • 在Kotlin中,public是預設可見性,可省略
    • 因此可以直接呼叫相關屬性,但其根本也是呼叫了getter
    //Kotlin中建立物件不需要new
    val person = Person("Bob", true)
    //在其他類中可以直接呼叫屬性
    println(person.name)
    
2、屬性
class Person (
    val name: String   //只讀屬性
    var isMarried: Boolean    //可寫屬性
)
  • Kotlin中這種簡潔的資料類宣告隱藏了和原始Java程式碼相同的底層實現:它是一個欄位都私有的類

  • 關鍵字:

    • (1)val關鍵字宣告的變數為只讀屬性
      • 生成一個欄位
      • 生成一個簡單的getter
    • (2)var關鍵字宣告的是可寫屬性
      • 生成一個欄位
      • 生成一個getter
      • 生成一個setter
3、自定義訪問器
class Rectangle(val height: Int, val width: Int) {
    val isSquare: Boolean
        //自定義isSquare屬性的getter
        //法1:
        get() {
            return height == width
        }
        或
        //法2:
        get() = height == width
}
4、Kotlin原始碼佈局:目錄和包
  • Kotlin不區分匯入的是類還是函式

    • 可以只匯入某個類的某個函式
    • 可以在包名稱後加上.*來匯入特定包中定義的所有宣告
  • Kotlin中,可以把多個類放在同一個檔案中

三、表示處理和選擇:列舉和“when”

1、宣告列舉類
  • Kotlin中,enum是一個軟關鍵字,即只有在class前使用才會有特殊含義,其他地方可以當作普通名稱使用
enum class ColorEnum(var r: Int, val g: Int, val b: Int) {
    //初始化列舉物件(注意保持引數一致)
    RED(255, 0, 0), ..., ..., ...;
    
    
    //定義其他方法
    
}
  • 初始化列舉物件時是kotlin中唯一需要使用分號的地方
    • 使用分號將列舉常量列表和方法定義分開
2、使用when關鍵字處理列舉累(相當於switch
  • when是一個又返回值的表示式,可以寫一個直接返回when表示式的表示式體函式
fun getMnemonic(color: Color) = 
    when (color) {
        
        Color.RED -> "紅色"
        Color.ORANGE -> "橙色"
        ...
        
    }
  • 可以將多個條件值合併到同一個分支
    • 條件值之間通過逗號隔開
    • 等同於||
fun getWarmth(color: Color) = when(color) {
    Color.RED, Color.ORANGE, Color.YELLEW -> "紅色或橙色或黃色"
    Color.GREEN -> "綠色"
    ···
}
  • 匯入列舉常量省略列舉類名
improt ch2.colors.Color
import ch2.colors.Color.*

fun getWarmth(color: Color) = when(color) {
    Color.RED, Color.ORANGE, Color.YELLEW -> "紅色或橙色或黃色"
    Color.GREEN -> "綠色"
    ···
}
3、在when中使用任意物件
  • kotlin中,when結構允許使用任意型別的物件
4、使用不帶引數的when
  • 如果沒有給when表示式提供引數,粉質條件就是任意的布林表示式
when {
    (a == b) || (b == c) -> "a等於b或b等於c"
    ...
}
5、智慧轉換:合併型別檢查和轉換(相當於“多型”)
  • (1)宣告類的繼承及實現關係

    • 宣告類的時候,使用一個冒號(:)後面跟上介面名稱,來標記這個類實現了這個介面
    • 通過被繼承或實現的類定義時是class還是interface來定義:表示的是繼承還是實現
    • kotlin同樣是“單繼承,多實現”
  • (2)通過is檢查變數型別(相當於Java中的instanceOf

    • 智慧轉換:當一個物件通過is檢查過型別後,即可直接呼叫該物件的相關成員
      • 在Java中,instanceOf檢查完後還需要一次型別轉換才能直接呼叫物件的成員,而Kotlin通過智慧轉換省略了這一步
if (e is Sum) {
    return println("$e.right, ${e.left)
}
  • (3)通過as進行顯式的型別轉換
    • eg:val n = e as Num(將e轉為Num型別)
6、重構:用“when”代替“if”
  • (1)if表達

    • 如果if分支中只有一個表示式,花括號可以省略
    • 如果if分支是一個程式碼塊,則程式碼塊中的最後一個表示式會被作為結果返回
  • (2)when中的else(等同於Java中switch的default)

fun eval(e: Expr): Int = 
    when (e) {
        is Num ->  //is xx 用於檢查
            e.value  //
        is Sum ->
            eval(e.right) + eval(e.left)
        else ->
            throw IllegalArgumentException("Unknown expression")
    }
7、程式碼塊作為“if”和“when”分支
  • 對於if、when、try/catch結構來說,如果是程式碼塊體表示,最後一個表示式就是結果(返回值)

四、迭代事物:“while”迴圈和“for”迴圈

1、for迴圈
  • kotlin中,for僅以唯一一種形式存在for <item> in <elements>(類似於for each
for(i in 1..100) {  //其中i在使用時宣告
    ...
}
2、while迴圈
  • (1)while
    • 當condition為true時執行迴圈體
while (condition) {
    ... 
}
  • (2)do..while
    • 迴圈體第一次會無條件的執行
    • 從第二次開始只有當condition為true時才執行
do {
    ...
} whlie (condition)
3、迭代數字:區間和數列
  • (1)區間

    • 定義:區間本質上就是兩個值之間的間隔,這兩個值通常是數字。一個起始值,一個結束值。
    • 關鍵字:使用..運算子表示區間
    • 示例:val onToTen = 1..10
    • 注意:Kotlin的區間是包含或閉合的,意味著始終包含結束值
  • (2)數列

    • 定義:整數區間,且能迭代區間中的所有值,這樣的區間被稱作數列
  • (3)帶步長的數列

    • 關鍵字:downTostep
    • 特性:允許跳過一些數字。步長可以是負數
    • 示例:
      for(i in 100 downTo 1 step 2) {
          //從100開始遞減,由遞減1變為遞減2,
      }
      
  • (3)半閉合區間

    • 定義:不包含結束值的區間
    • 關鍵函式:until
    • 示例:for(x in 0 until size)等價於for(x in 0..size - 1)
4、迭代map
  • 區間允許迭代:

    • 數字
    • 字元
    • 展開迭代集合中的元素
  • 展開迭代map

    • 可以使用map[key]讀取值,並使用map[key] = value設定他們
val binaryReps = TreeMap<char, String>()

//插入資料
for(c in "A".."F") {
    val binary = Integer.toBinaryString(c.toInt())
    binaryReps[c] = binary  //根據鍵c將值儲存到map中
}

for ((letter, binary) in binaryReps) { 
    //將
    println("$letter = $binary")
}
  • 展開迭代List
    • list.withIndex()
val list = arrayListOf("10", "11", "1001")
for ((index, element) in list.withIndex()) {
    println("$index: $element")
}
5、使用“in”檢查集合和區間的成員
  • 使用in運算子或它的逆運算子!in來檢查這個值是否在區間中

  • 區間不僅限於字元和數字

    • 假如有一個支援例項比較操作的任意類(實現了java.lang.Comparable介面),就能建立這種型別的物件區間
  • in檢查同樣適用於集合

    println("Kotlin" in setOf("Java", "Scala"))
    輸出:false
    

五、Kotlin中的異常

  • Kotlin中throw結構是一個表示式,能作為另一個表示式的一部分使用
1、“try”、“catch”和“finally”
  • Kotlin並不區分受檢異常和未受檢異常

    • 不用制定函式丟擲的異常
    • 不強制處理異常
  • 所以throw子句不一定會出現在程式碼中

2、 “try”作為表示式
  • try可引入一個表示式,將它的值賦給一個變數
    • 總是需要用花括號將語句主題括起來
    fun readNumber(reader: BufferedReader) {
        val number = try {                            //將“try”作為表示式賦值給number
            Integer.parseInt(reader.readLine())
        } catch (e: NumberFormatException) {
            return
        }
        
        println(number)
    }