從點(diǎn)滴基礎(chǔ)探究Kotlin的獨(dú)特魅力

0. 序言

  • 從接觸Rxjava了解到函數(shù)式編程,從了解函數(shù)式編程想起Lambda表達(dá)式,從Lambda表達(dá)式接觸到"Lambda表達(dá)式遇上Kotlin",這一次我是真的愛上了Kotlin,代碼比Java更加簡潔的同時(shí)卻不晦澀.
  • 再次接觸Kotlin,對Kotlin的新特性又有了一個(gè)更加深刻的認(rèn)識: Kotlin可謂取眾多語言之精華!這一次我從愛變成了癡迷.
  • 了解新特性,請?zhí)D(zhuǎn) : http://www.itdecent.cn/p/9a419b60e2c5 而探究Kotlin獨(dú)特魅力,要從細(xì)節(jié)入手,這篇博文從以下幾個(gè)方面講述Kotlin是如何的獨(dú)具魅力!

1. 目錄

  • 剖析方法結(jié)構(gòu)
  • 函數(shù)
  • 變量
  • 字符串模板
  • 注釋
  • 自定義訪問器
  • 源碼布局:目錄和包
  • 表示和處理選擇:枚舉和"when"
  • 迭代事物:"while"和"for"循環(huán)
  • 異常
  • 綜上(熟悉kotlin的,可以直接跳轉(zhuǎn)至綜上)

2. 剖析方法結(jié)構(gòu)

fun main(args:Array<String>){
    println("Hello World")
}

① fun:聲明函數(shù)
② 參數(shù)的類型在名稱后面
③ 函數(shù)可以定義在外層,沒有必要放在類中
④ 數(shù)組就是類.kotlin中沒有聲明數(shù)組類型的語法
⑤ kotlin標(biāo)準(zhǔn)庫含有針對java標(biāo)準(zhǔn)庫函數(shù)進(jìn)行的簡潔的包裝:println就是一個(gè)
③ 沒有分號

3. 函數(shù)

  • 帶有兩個(gè) Int 參數(shù)、返回 Int 的函數(shù)
fun sum(a: Int, b: Int): Int {
        return a + b
}
println(sum(1,2))
06-06 12:39:11.123 1993-1993/com.best.chapter_01 I/System.out: 3
  • 返回值是函數(shù)體的函數(shù)
fun max(a: Int, b: Int): Int {
        return if(a>b) a else b 
}
println(max(1,2))
06-06 12:43:22.632 2081-2081/? I/System.out: 2

說明:
① kotlin中,if語句是有結(jié)果值的表達(dá)式,不是語句,類似于三元運(yùn)算符.
② kotlin中,除了循環(huán)(for do 和 do while)以外大多數(shù)控制結(jié)構(gòu)都是表達(dá)式.
③ 語句和表達(dá)式的區(qū)別:表達(dá)式有值,并且能作為另一個(gè)表達(dá)式的一部分使用;語句總是包圍著它的代碼中的頂層元素,并且沒有自己的值.

  • 表達(dá)式函數(shù)體
fun max(a: Int, b: Int): Int = if (a > b) a else b

說明:
① 如果函數(shù)體是由單個(gè)表達(dá)式構(gòu)成,可以用這個(gè)表達(dá)式作為完整的函數(shù)體,并去掉花括號和return語句.
② 如果函數(shù)體是在花括號中,我們說這個(gè)函數(shù)由代碼塊體;如果它直接返回一個(gè)表達(dá)式,它就有表達(dá)式體.
③ IDEA提供表達(dá)式函數(shù)體與代碼塊函數(shù)體之間的轉(zhuǎn)換意向動作.

  • 返回值類型自動推斷:
fun max(a: Int, b: Int) = if (a > b) a else b

說明:
① 對于表達(dá)式函數(shù)來說:編譯器會分析作為函數(shù)體的表達(dá)式,并把它的類型作為函數(shù)的返回類型,即使沒有顯式地寫出來.這種分析通常被稱為類型推導(dǎo).
② 只有表達(dá)式體函數(shù)的返回類型可以省略,代碼塊體函數(shù)的返回類型和return語句必須顯示寫出來.

  • 返回?zé)o意義的值
    fun printSum(a: Int, b: Int) :Unit{
        println("sum of $a and $b is ${a + b}")
    }

    fun printSum(a: Int, b: Int) {
        println("sum of $a and $b is ${a + b}")
    }
06-07 11:40:34.006 1844-1844/? I/System.out: sum of 1 and 2 is 3
06-07 11:40:34.006 1844-1844/? I/System.out: kotlin.Unit

4. 變量

  • val和var
    ① val:對應(yīng)的是Java的final變量,不可變引用,不能初始化之后再次賦值.
    ② var:對應(yīng)的是非final變量.
    ③ 盡可能地使用val關(guān)鍵字來聲明所有的kotlin變量,僅在必要的時(shí)候換成var.使用不可變變量以及不可變對象及無副作用的函數(shù)讓你的代碼更接近函數(shù)式編程風(fēng)格.
  • 一次賦值(只讀)的局部變量:
val a: Int = 1  // 立即賦值
val b = 2   // 自動推斷出 `Int` 類型
val c: Int  // 如果沒有初始值類型不能省略
c = 3       // 明確賦值
  • 可變變量:
var x = 5 // 自動推斷出 `Int` 類型
x += 1
  • 頂層變量:
val PI = 3.14
  • var關(guān)鍵字允許變量改變自己的值,但它的類型卻是改變不了的.
        var answer = 42
        var answer = "HELLO"

說明:編譯報(bào)錯(cuò):編譯器只會根據(jù)初始化器來推斷變量的類型,在決定類型的時(shí)候不會考慮后續(xù)的賦值操作.

5. 字符串模板

  • 簡單的變量名稱
var a = 1
val s1 = "a is $a" 
println(s1)
06-09 21:42:37.708 2092-2092/? I/System.out: a is 1
fun main(args: Array<String>) {
    var name = if(args.size>0) args[0] else "kotlin"
    println("Hello,$name!")
}
Hello,kotlin!
  • 任意表達(dá)式
fun printSum(a: Int, b: Int){
      println("sum of $a and $b is ${a + b}")
}
println(printSum(1, 2))
06-09 21:40:22.212 2007-2007/? I/System.out: sum of 1 and 2 is 3
  • 雙引號嵌套雙引號(只要它們處在某個(gè)表達(dá)式的范圍內(nèi),即花括號內(nèi))
 fun printSum(a: Int, b: Int){
        println("Hello,${if (a >b) a else "other" }")
    }
printSum(1,2)
06-09 22:27:54.452 2465-2465/com.best.chapter_01 I/System.out: Hello,other

6. 注釋

// 這是一個(gè)行注釋

/* 這是一個(gè)多行的
   塊注釋。 */

7. 類和屬性

class Person(var name: String, var isMarried: Boolean)

說明: 這種類(只有數(shù)據(jù)沒有代碼)通常叫做值對象(不好理解,先擱置,回頭再看)

  • 屬性
class Person(var name: String, var isMarried: Boolean)

說明:
① kotlin中屬性是頭等的語言特性,完全代替了字段和訪問器方法.
② 聲明一個(gè)屬性和聲明一個(gè)變量一樣:使用val和var關(guān)鍵字:val代表屬性是可讀的,var代表屬性是可變的.
③ 當(dāng)你聲明屬性的時(shí)候,就聲明了對應(yīng)的訪問器(只讀屬性有一個(gè)setter,而可寫屬性既有g(shù)etter也有setter)

  • Kotlin使用Person類
        val person = Person("Green", true);
        println(person.name)
        println(person.isMarried)

① 創(chuàng)建對象不用new
② 引用屬性,不再需要調(diào)用getter和setter
③ setter的具體操作方法:

        val person = Person("Green", true);
        println(person.name)
        println(person.isMarried)
        person.name = "Tom"
        person.isMarried = false
        println(person.name)
        println(person.isMarried)

8.自定義訪問器

/**
 * Created by FuKaiqiang on 2018-06-04.
 */
class Rectangle(var width: Int, var height: Int) {
    var isRectanggle: Boolean = false 
        get() = width == height //聲明屬性的getter
}
 val rectangle = Rectangle(5, 5)
        println(rectangle.isRectanggle)
06-10 01:54:49.759 3355-3355/? I/System.out: true

9. 源碼布局:目錄和包

- 包名: com.example.baidu
  - 目錄: 
    - ui: MainActivity SpashActivity
    - util : AppUtil
    - widget
    - entity
  ...

說明:
① 包名下有目錄,目錄下有類,類中有方法等等.
② 每個(gè)類都有自己的包名,每個(gè)方法都有的引用路徑.
③ 如上則是: Java中,目錄層次結(jié)構(gòu)照搬了包層次結(jié)構(gòu)

- 包名:com.example.baidu
  - example.kt
  - shapes.kt

說明:
① kotlin中:包層次結(jié)構(gòu)不需要遵循目錄層次結(jié)構(gòu).
② kotlin中:可以把多個(gè)類放在同一個(gè)文件中,并且文件的名字可以隨意定義,也沒有對磁盤上源文件的布局強(qiáng)加任何限制.
③ 因?yàn)槊總€(gè)類代碼都很小,所以kotlin建議把眾多類放在一個(gè).kt文件中即可.

10. 表示和處理選擇:枚舉和"when"

  • 強(qiáng)調(diào): kotlin中使用"when"來替代java中的"switch".
  • 聲明枚舉類:
enum class Colors {
    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}

說明:
① 極少數(shù)kotlin聲明比java使用了更多的關(guān)鍵字的例子:kotlin用了enum class 兩個(gè)關(guān)鍵字,而java只有enum一個(gè)關(guān)鍵字.
② enum是一個(gè)所謂的軟關(guān)鍵字:只有當(dāng)它出現(xiàn)在class前面時(shí)才有特殊的意義,在其他地方可以把它當(dāng)做普通的名稱使用.

  • 枚舉并不是值得代表,可以給枚舉類聲明屬性和方法
/**
 * Created by FuKaiqiang on 2018-06-10.
 */
enum class Color(val r: Int, val g: Int, val b: Int) { // 聲明枚舉常量的屬性
    RED(255, 0, 0), ORANGE(255, 165, 0), YELLOW(255, 255, 0),
    GREEN(0, 255, 0), BLUE(0, 0, 255), INDIGO(75, 0, 130), VIOLET(238, 130, 238); // 在常量創(chuàng)建的時(shí)候指定屬性值,這里必須有分號

    fun rgb() = (r * 256 + g) * 256 + b // 給枚舉類定義一個(gè)方法
}

說明:
① 枚舉常量在聲明的時(shí)候,必須提供常量的屬性值.
② 這個(gè)例子展示了 kotlin 中唯一需要使用分號的地方:如果在枚舉中定義任何方法,就要使用分號把枚舉常量列表和方法定義分開.

  • 使用"when"處理枚舉類
    fun getMnemonic(color:Color) = //直接返回一個(gè)When表達(dá)式
            when(color){ // 如果顏色和枚舉常量相等就返回對應(yīng)的字符串
                Color.RED ->"Richard"
                Color.ORANGE ->"Of"
                Color.YELLOW ->"York"
                Color.GREEN ->"Gave"
                Color.BLUE ->"Battle"
                Color.INDIGO ->"In"
                Color.VIOLET ->"Vain"
            }
println(getMnemonic(Color.GREEN))
06-10 10:50:24.277 5279-5279/? I/System.out: Gave

說明:
① when 是一個(gè)有返回值的表達(dá)式,這里直接返回when表示式的表達(dá)式函數(shù)體.
② 你不用書寫break,避免因?yàn)槁慴reak而導(dǎo)致的bug.
③ 可以把多個(gè)值合并到同一個(gè)分支,只需要用逗號隔開這些值.

 fun getWarmth(color: Color) = when (color) {
        Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
        Color.GREEN -> "neutral"
        Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold"
    }
 println(getWarmth(Color.ORANGE))
06-10 10:57:23.959 5374-5374/com.best.chapter_01 I/System.out: warm

說明:
① 上面這個(gè)例子用的是枚舉的完整名稱,即指定了枚舉類的名稱Color.
② 可以通過導(dǎo)入這些常量值和常量值所在的類的方法來簡化代碼.

import com.best.chapter_01.Color.* // 顯式地導(dǎo)入枚舉常量就可以使用它們的名稱
fun getWarmth(color: Color) = when (color) {
        RED, ORANGE, YELLOW -> "warm"
        GREEN -> "neutral"
        BLUE, INDIGO, VIOLET -> "cold"
    }
println(getWarmth(Color.GREEN))
06-10 11:02:05.599 5462-5462/com.best.chapter_01 I/System.out: neutral
  • "when"結(jié)構(gòu)中使用任意對象
import com.best.chapter_01.Color.*
    fun mix(c1: Color, c2: Color) =
            when (setOf(c1, c2)) { // when表達(dá)式的實(shí)參可以是任何對象,它被檢查是否與分支條件相等
                setOf(RED, YELLOW) -> ORANGE // 列舉出能夠混合的顏色對
                setOf(YELLOW, BLUE) -> GREEN
                setOf(BLUE, VIOLET) -> INDIGO
                else -> throw Exception("Dirty color") // 如果沒有任何其他分支匹配這里就會執(zhí)行
            }
println(mix(RED, YELLOW))
06-10 11:09:59.130 5561-5561/? I/System.out: ORANGE

說明:
① java中的switch要求必須使用常量(枚舉常量\字符串\數(shù)字字面值)作為分支條件.
② kotlin廢棄了switch,使用when代替switch,允許使用任何對象.
③ kotlin標(biāo)準(zhǔn)庫中有setOf函數(shù),可以創(chuàng)建出一個(gè)Set,會包含所有指定為函數(shù)實(shí)參的對象.條目順序不重要,只要兩個(gè)set中包含一樣的條目,它們就是相等的.
④ 上面這些例子都是等式檢查,接下來會看到條件還可以是任意的布爾表達(dá)式.

  • 使用不帶參數(shù)的"when"
    fun mixOptimized(c1: Color, c2: Color) =
            when {  // 沒有實(shí)參傳給when
                (c1 == RED && c2 == YELLOW) ||
                        (c2 == RED && c1 == YELLOW) -> ORANGE
                (c1 == YELLOW && c2 == BLUE) ||
                        (c2 == YELLOW && c1 == BLUE) -> GREEN
                (c1 == BLUE && c2 == VIOLET) ||
                        (c2 == BLUE && c1 == VIOLET) -> INDIGO
                else -> throw Exception("Dirty color") // 如果沒有任何其他分支匹配這里就會執(zhí)行
            }
println(mixOptimized(BLUE, YELLOW))
06-16 22:43:26.391 1959-1959/? I/System.out: GREEN

說明:
① 如果每次調(diào)用函數(shù)的時(shí)候,就創(chuàng)建一些Set實(shí)例,那創(chuàng)建頻繁就會出問題.
② 如果when表達(dá)式?jīng)]有參數(shù),分支條件就是任意的布爾表達(dá)式.這樣可讀性差一些,但是性能更好.

  • 智能轉(zhuǎn)換: 合并類型檢查和轉(zhuǎn)換
interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr
fun eval(e: Expr): Int {
   if (e is Num) {
       val n = e as Num //顯示地轉(zhuǎn)換成類型Num 是多余的
       return n.value
   }
   if (e is Sum) {
       return eval(e.right) + eval(e.left) // 變量e被智能地轉(zhuǎn)換了類型
   }
   throw IllegalArgumentException("Unknown expression")
}
println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))

說明:
① 檢查過一個(gè)變量是某種類型,后面就不再需要轉(zhuǎn)換它,可以就把它當(dāng)作你檢查過的類型使用.實(shí)際上編譯器進(jìn)行了類型轉(zhuǎn)換,這種行為稱為智能轉(zhuǎn)換.
② 使用 as 關(guān)鍵字來表示到特定類型的顯示轉(zhuǎn)換.
③ 當(dāng)你對一個(gè)類的屬性進(jìn)行智能轉(zhuǎn)換的時(shí)候,這個(gè)屬性必須是一個(gè)val屬性,而且不能有自定義的訪問器.
④ 經(jīng)過智能轉(zhuǎn)換的值會用不同的背景顏色著重表示,這樣就更容易發(fā)現(xiàn)這個(gè)值是事先檢查過的.

  • 重構(gòu): 用"when" 代替 "if"
    kotlin沒有三元運(yùn)算符,因?yàn)閕f表達(dá)式有返回值.而當(dāng)我們使用if的時(shí)候,可以使用表達(dá)式語法: 去掉return語句和花括號,使用if表達(dá)式作為函數(shù)體.
  1. 使用有返回值的 if 表達(dá)式
    省略return
    fun eval(e: Expr): Int =
            if (e is Num) {
                e.value
            } else if (e is Sum) {
                eval(e.right) + eval(e.left)
            } else {
                throw IllegalArgumentException("Unknown expression")
            }
    println(eval(Sum(Num(1), Num(2))))
    06-17 00:31:37.587 2492-2492/com.best.chapter_01 I/System.out: 3
    省略花括號
    fun eval(e: Expr): Int =
            if (e is Num) e.value
            else if (e is Sum) eval(e.right) + eval(e.left)
            else throw IllegalArgumentException("Unknown expression")
  1. 使用 "when" 代替 if 層疊
    fun eval(e: Expr): Int =
            when (e) {
                is Num -> e.value // 應(yīng)用了 智能轉(zhuǎn)換 檢查實(shí)參類型的"when"分支
                is Sum -> eval(e.left) + eval(e.right)
                else -> throw IllegalArgumentException("Unknown expression")
            }

說明:
① when表達(dá)式并不僅限于檢查值是否相等----這里使用了另一種 when 分支的形式,允許你檢查 when 實(shí)參值的類型.
② 不過當(dāng)邏輯過于復(fù)雜的時(shí)候,可以使用代碼塊作為分支體.

  • 代碼塊作為 "if" 和 "when" 的分支
    fun evalWithLogging(e: Expr): Int =
            when (e) {
                is Num -> {
                    println("num:   ${e.value}")
                    e.value // 代碼塊中最后的表達(dá)式就是代碼塊分支體的結(jié)果
                }
                is Sum -> {
                    val left = evalWithLogging(e.left)
                    val right = evalWithLogging(e.right)
                    println("sum:   $left +$right")
                    left + right // 如果 e 的類型是 Sum 就會返回這個(gè)表達(dá)式
                }
                else -> throw IllegalArgumentException("Unknown expression")
            }
06-17 01:01:02.375 2872-2872/com.best.chapter_01 I/System.out: num:   1
06-17 01:01:02.375 2872-2872/com.best.chapter_01 I/System.out: num:   2
06-17 01:01:02.375 2872-2872/com.best.chapter_01 I/System.out: sum:   1 +2
06-17 01:01:02.375 2872-2872/com.best.chapter_01 I/System.out: 3

說明:
① 代碼塊中最后的表達(dá)式就是結(jié)果,在所有使用代碼塊并期望得到一個(gè)結(jié)果的地方成立.
② 這個(gè)規(guī)則對常規(guī)函數(shù)不成立 : 一個(gè)函數(shù)體要么具有不是代碼塊的表達(dá)式函數(shù)體,要么具有包含具體return 語句的代碼塊函數(shù)體.

11. 迭代循環(huán): "while" 循環(huán)和"for" 循環(huán)

  • "while" 循環(huán) ( 無新特性 可略過)
while (condition){ // 當(dāng) condition 為 true 時(shí)執(zhí)行循環(huán)體
    /*...*/
}
do {
  /*...*/
}while (condition) // 循環(huán)體第一次會無條件地執(zhí)行.此后,當(dāng)condition 為true時(shí)才執(zhí)行
  • 迭代數(shù)字: 區(qū)間和數(shù)列
var oneToTen = 1..10

說明:
① kotlin 沒有常規(guī)的 for 循環(huán) ,而用 區(qū)間 替代.
② 區(qū)間本質(zhì)上是兩個(gè)值之間的間隔,這兩個(gè)值通常是數(shù)字: 一個(gè)是起始值 , 一個(gè)是結(jié)束值.使用 in 運(yùn)算符表示區(qū)間.
③ kotlin的區(qū)間是包含的,即閉合的,意味著第二個(gè)值始終是區(qū)間的一部分.
④ 整數(shù)區(qū)間做的最基本的事情就是循環(huán)迭代其中所有的值.如果你能迭代區(qū)間中所有的值,這樣的區(qū)間被稱作數(shù)列.

fun main(args: Array<String>) {
   for (i in 1 .. 100) {
            print(fizzBuzz(i))  
   }
}

fun fizzBuzz(i: Int) = when {
        i % 15 == 0 -> "FizzBuzz "
        i % 3 == 0 -> "Fizz "
        i % 5 == 0 -> "Buzz "
        else -> "$i "
    }

1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 ...

說明: 在Android Activity中結(jié)果不會輸出,不知道為什么,但是println卻可以.

for (i in 100 downTo 1 step 2) {
      print(fizzBuzz(i))
}

Buzz 98 Fizz 94 92 FizzBuzz 88 86 Fizz 82 ...

說明:
① 這是在迭代一個(gè)帶步長的數(shù)列,它允許跳過一些數(shù)字.
② 步長也可以負(fù)數(shù),這種情況下數(shù)列是遞減而不是遞增的.
③ 100 downTo 1 : 遞減數(shù)列(步長為-1).step 把步長的絕對值變成了2 , 但方向保持不變(事實(shí)上,步長被設(shè)置成了 -2) .
④ .. 語法是閉合區(qū)間,而如果想使用半閉合區(qū)間(不包含指定結(jié)束的值),使用util函數(shù)即可. for ( x in 0 util size) == for ( x in 0 .. size - 1)

  • 迭代 map
  1. 初始化并迭代 map
        var binaryReps = TreeMap<Char, String>() // 使用TreeMap讓鍵排序
        for (c in 'A'..'F') { // 使用字符區(qū)間迭代從A到F之間的字符
            var binary = Integer.toBinaryString(c.toInt()) // 把ASCII碼轉(zhuǎn)換成二進(jìn)制
            binaryReps[c] = binary // 根據(jù)鍵c把值存儲到map中
        }
        for ((letter, binary) in binaryReps) { // 迭代map,把鍵和值賦給兩個(gè)變量
            println("$letter = $binary")
        }
        06-17 04:06:11.919 4184-4184/? I/System.out: A = 1000001
        06-17 04:06:11.919 4184-4184/? I/System.out: B = 1000010
        06-17 04:06:11.919 4184-4184/? I/System.out: C = 1000011
        06-17 04:06:11.919 4184-4184/? I/System.out: D = 1000100
        06-17 04:06:11.919 4184-4184/? I/System.out: E = 1000101
        06-17 04:06:11.919 4184-4184/? I/System.out: F = 1000110

說明:
① .. 語法不僅可以創(chuàng)建數(shù)字區(qū)間,還可以創(chuàng)建字符區(qū)間.這里迭代從A到F的字符.
② for 循環(huán)允許展開迭代中的集合的元素(這里是 map 鍵值對集合) : 把展開的結(jié)果存儲到了兩個(gè)獨(dú)立的變量中 : letter 是鍵, binary 是值.
③ 可以根據(jù)鍵來訪問和更新map : 使用map[key] 讀取值 , 并使用 map[key] = value 設(shè)置值,而不需要調(diào)用 get 和 put .

  1. 展開語法在迭代集合的同時(shí)跟蹤當(dāng)前項(xiàng)的下標(biāo)
        var list = arrayListOf("10", "11", "1001")
        for ((index, element) in list.withIndex()) {
            println("$index: $element")
        }
      06-17 04:18:11.968 4300-4300/com.best.chapter_01 I/System.out: 0: 10
      06-17 04:18:11.968 4300-4300/com.best.chapter_01 I/System.out: 1: 11
      06-17 04:18:11.968 4300-4300/com.best.chapter_01 I/System.out: 2: 1001
  • 使用 "in" 檢查集合和區(qū)間的成員
    fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
    fun isNotDigit(c: Char) = c !in '0'..'9'
    println(isLetter('q'))
    println(isNotDigit('x'))

說明:
① in 運(yùn)算符來檢查一個(gè)值是否在區(qū)間中 , 或者 它的逆運(yùn)算 !n 來檢查這個(gè)值是否不在區(qū)間中.
② in 簡潔的隱藏了標(biāo)準(zhǔn)庫中的區(qū)間類的具體的實(shí)現(xiàn)邏輯

c in 'a' .. 'z'  變換成 a <= c && c <= z

③ in 也適用于 when 表達(dá)式

    fun recognize(c: Char) = when(c){
        in '0'..'9' ->"It is a digit !"
        in 'a'..'z',in 'A'..'Z' ->"It is a letter!" // 可以組合多種區(qū)間
        else -> "I don't know..."
    }
    println(recognize('8'))
    06-20 10:59:29.402 3321-3321/com.best.chapter_01 I/System.out: It is a digit !

④ in 同樣適用于集合

println("kotlin" in setOf("java","scala"))
06-20 11:02:01.495 3435-3435/? I/System.out: false

⑤補(bǔ)充: 區(qū)間不僅限于字符.也適用于支持實(shí)例比較操作的任意類(實(shí)現(xiàn)了java.lang.Comparable接口)

println("kotlin" in "java".."scala")
06-20 11:07:11.911 3527-3527/com.best.chapter_01 I/System.out: true

說明: 這種情況下,雖然不能列舉出"java"和"kotlin"之間的字符串,但是仍然可以使用in 運(yùn)算符檢查一個(gè)其他對象是否屬于這個(gè)區(qū)間.

12. kotlin 中的異常

與java類似,方法的調(diào)用者能捕獲到這個(gè)異常并處理它;如果沒有被處理,異常會沿著調(diào)用棧再次拋出.

        val percentage = 110
        if (percentage !in 0..100) {
            throw IllegalArgumentException("A percentage value must be between 
            0 and100: $percentage")
        }
        java.lang.IllegalArgumentException: A percentage value must be between 
        0 and 100: 110

說明:
① 不用new 關(guān)鍵字來創(chuàng)建異常實(shí)例.
② kotlin 中 throw 結(jié)構(gòu)是一個(gè)表達(dá)式,可以作為另一個(gè)表達(dá)式的一部分使用:

        var number = 50
        val percentage =
                if (number in 0..100)
                    number
                else
                    throw IllegalArgumentException("A percentage value must be between 
                    0 and100: $number")
        println(percentage)
        06-20 11:22:06.025 3988-3988/com.best.chapter_01 I/System.out: 50
  • "try" "catch" 和 "finally"
  1. 像在java中一樣使用 "try"
    fun readNumber(reader: BufferedReader): Int? { // 不必顯式地指定這個(gè)函數(shù)可能拋出的異常
        try {
            val line = reader.readLine()
            return Integer.parseInt(line)
        } catch (e: NumberFormatException) { // 異常類型在右邊
            return null // 發(fā)生異常的情況下使用的值
        } finally {
            reader.close()
        }
    }
        var reader = BufferedReader(StringReader("123"))
        println(readNumber(reader))
        06-20 11:28:57.529 4157-4157/? I/System.out: 123

說明:
① 和Java最大的區(qū)別是不用顯示地拋出異常.
② kotlin 不區(qū)分受檢異常和未受檢異常.不用指定函數(shù)拋出的異常,而且可以處理也可以不處理異常. 受檢異常: 比如 IOException就是一個(gè)受檢異常.

  1. "try" 作為表達(dá)式
    fun readNumber(reader: BufferedReader) {
        var number = try {
            Integer.parseInt(reader.readLine()) // 沒有異常發(fā)生時(shí)使用這個(gè)值
        } catch (e: NumberFormatException) {
            null // 發(fā)生異常的情況下使用的值
        }
        println(number)
    }
    var reader = BufferedReader(StringReader("not a number"))
    readNumber(reader)
    06-20 11:37:49.109 4262-4262/com.best.chapter_01 I/System.out: null

說明:
① 不同于if,try 引入表達(dá)式以后總是需要用花括號把語句主體括起來.
② 和其他語句一樣,如果其主體包含多個(gè)表達(dá)式,那么整個(gè)try表達(dá)式的值就是最后一個(gè)表達(dá)式的值 , 比如 catch 里面的 null

13. 綜上

① fun 關(guān)鍵字用來聲明函數(shù) . val 關(guān)鍵字和 var 關(guān)鍵字分別用來聲明只讀變量和可變變量.
② 字符串模板幫你避免繁瑣的字符串連接. 在變量名稱前加上 前綴 或者 用{ }包圍一個(gè)表達(dá)式,來把值注入到字符串中.
③ 值對象類在kotlin中簡潔的方式表示.
④ kotlin 中的 if 是帶返回值的表達(dá)式.
⑤ when 表達(dá)式替代了 java 中的 switch ,而且更強(qiáng)大.
⑥ 檢查過變量具有某種類型之后不必顯示地轉(zhuǎn)換它的類型 : 這就叫 智能轉(zhuǎn)換.
⑦ for 在kotlin中更加方便,特別是需要迭代map的時(shí)候,又或者迭代集合需要下標(biāo)的時(shí)候.
⑧ 1..5 會創(chuàng)建一個(gè)區(qū)間. 可以使用 in 運(yùn)算符和 !in運(yùn)算符來檢查值是否屬于某個(gè)區(qū)間.
⑨ kotlin 中的異常和java相似,除了kotlin不要求你聲明函數(shù)可以拋出的異常.

14. 后續(xù)

如果大家喜歡這篇文章,歡迎點(diǎn)贊;如果想看更多 kotlin 方面的技術(shù),歡迎關(guān)注!

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

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

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