《Kotlin实战》读书笔记第二章
Kotlin基礎
1.基本要素:函數(shù)和變量
函數(shù)
語句和表達式:
在Kotlin中,if是表達式而不是語句。
- 表達式:有值,能作為另一個表達式的一部分使用
- 語句:無值,總是包圍著它的代碼塊中的頂層元素
在Java中,所有控制結構都是語句。
在Kotlin中,除了循環(huán)以外的大多數(shù)控制結構都是表達式。
在Java中,賦值是表達式,在Kotlin中則是語句。這有助于避免比較和賦值之間的混淆。
表達式函數(shù)體:
函數(shù)體由單個表達式構成的函數(shù)。
用表達式作為完整的函數(shù)體,并去掉花括號和return語句。
fun max(a: Int,b: Int) = if(a > b) a else b變量
聲明變量關鍵字由兩個:
- val:不可變引用,來自value。不能在初始化之后再次賦值,對應final變量
- var:可變引用,來自variable。對應普通變量
默認情況下盡可能使用val關鍵字來聲明變量。
使用不可變引用,不可變對象,無副作用的函數(shù)讓你的代碼更接近函數(shù)式編程風格。
字符串模板
val name = "world" println("hello,$name")和許多腳本語言一樣,Kotlin可以在字符串字面值中引用局部變量,在變量前加上$即可。
這等價于Java中的字符串連接,編譯后的代碼創(chuàng)建了一個StringBuilder對象,并把常量和變量附加上去。
對象表達式和聲明的區(qū)別
他倆之間只有一個特別重要的區(qū)別:
-
對象表達式在我們使用的地方立即初始化并執(zhí)行的
-
對象聲明是懶加載的,是在我們第一次訪問時初始化的。
2.類和屬性
屬性
值對象:只有數(shù)據(jù)沒有其他代碼的類。
在Java中,字段和其訪問器的組合被叫做屬性
在Kotlin中,屬性是頭等的語言特性,在類中聲明一個屬性和聲明一個變量一樣:使用val和var關鍵字。
自定義訪問器
class Rectangle(val h: Int, val w: Int){val isSquare: Booleanget(){return h == w} }屬性isSquare不需要字段來保存其值,它的值是每次訪問屬性的時候被計算出來的。
也可以寫成表達式體get() = h == w
Kotlin源碼布局:目錄和包
Java把所有類組織成包,Kotlin也有類似的概念。
Kotlin不區(qū)分導入的是類還是函數(shù),允許使用import關鍵字導入任何種類的聲明。可以直接導入頂層函數(shù)的名稱。
在Java中,要把類放到和包結構相匹配的文件與目錄結構中。
在Kotlin中則不需要,可以把多個類放在同一個文件中,文件的名字還可以隨意選擇。也沒有對磁盤上源文件的布局強加任何限制。包的層級結構不需要遵循目錄的層級結構。
3.表示處理和選擇:枚舉和“when”
聲明枚舉類
枚舉并不是值的列表,可以給枚舉類聲明屬性和方法。
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);fun rgb() = (r * 256 + g) * 256 + b }fun main(args: Array<String>) {println(Color.BLUE.rgb()) }當你聲明了枚舉類的屬性,那么聲明枚舉常量的時候,必須提供此枚舉常量的屬性值。
**Kotlin中唯一必須使用分號的地方:**在枚舉類中,定義方法之前,用分號把常量和方法隔開。
使用“when”處理枚舉類
Java中switch語句,在Kotlin中對應的結構是when。
when是一個有返回值的表達式,因此可以直接寫一個返回when表達式的表達式體函數(shù)。
enum class Color {RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET }fun getMnemonic(color: Color) =when (color) {Color.RED -> "Richard"Color.ORANGE -> "Of"Color.YELLOW -> "York"Color.GREEN -> "Gave"Color.BLUE -> "Battle"Color.INDIGO -> "In"Color.VIOLET -> "Vain"}fun getWarmth(color: Color) = when(color) {Color.RED, Color.ORANGE, Color.YELLOW -> "warm"Color.GREEN -> "neutral"Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold" }fun main(args: Array<String>) {println(getMnemonic(Color.BLUE)) }和Java不一樣你不需要寫break語句,并且可以把多個值合并到同一個分支。
在“when”結構中使用任意對象
when允許使用任何對象作為分支條件。
fun mix(c1: Color, c2: Color) =when (setOf(c1, c2)) {setOf(RED, YELLOW) -> ORANGEsetOf(YELLOW, BLUE) -> GREENsetOf(BLUE, VIOLET) -> INDIGOelse -> throw Exception("Dirty color")}fun main(args: Array<String>) {println(mix(BLUE, YELLOW)) }使用不帶參數(shù)的“when”
上面一個例子中的代碼效率有點低,因為每次調(diào)用它都會創(chuàng)建一些Set實例。
可以換一種方式重寫:
fun mixOptimized(c1: Color, c2: Color) =when {(c1 == RED && c2 == YELLOW) ||(c1 == YELLOW && c2 == RED) ->ORANGE(c1 == YELLOW && c2 == BLUE) ||(c1 == BLUE && c2 == YELLOW) ->GREEN(c1 == BLUE && c2 == VIOLET) ||(c1 == VIOLET && c2 == BLUE) ->INDIGOelse -> throw Exception("Dirty color")}若沒有給when表達式提供參數(shù),則分支條件就是任意的布爾表達式。
智能轉換:合并類型檢查和轉換
接下來用一個函數(shù)做例子,此函數(shù)要求完成 (1+2)+4 的算術表達式的求值。
interface Expr class Num(val value: Int) : Expr class Sum(val left: Expr, val right: Expr) : Exprfun eval(e: Expr): Int {if (e is Num) {val n = e as Numreturn n.value}if (e is Sum) {return eval(e.right) + eval(e.left)}throw IllegalArgumentException("Unknown expression") }fun main(args: Array<String>) {println(eval(Sum(Sum(Num(1), Num(2)), Num(4)))) }上面的代碼展示了一種簡單的類結構來表達這種表達式編碼方式:Expr接口和其實現(xiàn)類Num和Sum
注意Expr接口沒有聲明任何方法,他只是一個標記接口,用來給不同種類的表達式提供一個公共的類型。
在Kotlin中,使用 is 檢查來判斷一個變量是否為某種類型。
is 檢查和Java中的 instanceOf 相似。但是Kotlin中若你檢查過某變量的類型,后面再使用它就不需要轉換了。
編譯器為你執(zhí)行了類型轉換,這種行為稱為智能轉換。
智能轉換只在變量經(jīng)過is檢查且之后不再發(fā)生變化的情況下有效。當你對一個類的屬性進行智能轉換的時候,這個屬性必須是val屬性,且不能有自定義的訪問器。否則每次對屬性的訪問是否都能返回同樣的值將無從驗證。
使用as關鍵字來表示到特定類型的顯示轉換。
重構:用“when”代替“if”
Kotlin沒有三元運算符,因為if表達式有返回值。
這意味著可以用表達式體語法重寫eval函數(shù)。
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")}若if分支只有一個表達式,花括號可以省略。
若if分支是代碼塊,代碼塊中最后一個表達式會被作為結果返回。
接下來使用when來重寫:
fun eval(e: Expr): Int =when (e) {is Num ->e.valueis Sum ->eval(e.right) + eval(e.left)else ->throw IllegalArgumentException("Unknown expression")}when表達式并不僅限于檢查值是否相等,它還允許你檢查實參值的類型。
代碼塊作為“if”和“when”的分支
分支體是代碼塊,代碼塊中最后一個表達式會被作為結果返回。
fun evalWithLogging(e: Expr): Int =when (e) {is Num -> {println("num: ${e.value}")e.value}is Sum -> {val left = evalWithLogging(e.left)val right = evalWithLogging(e.right)println("sum: $left + $right")left + right}else -> throw IllegalArgumentException("Unknown expression")}規(guī)則:代碼塊中最后的表達式就是結果
此規(guī)則對try主體和catch子句也有效。
此規(guī)則對常規(guī)函數(shù)不成立。一個常規(guī)函數(shù)要么具有不是代碼塊的表達式函數(shù)體,要么具有包含顯示return語句的代碼塊函數(shù)體。
4.迭代事物:“while”循環(huán)和“for”循環(huán)
while循環(huán)和Java中的沒有區(qū)別。
for循環(huán)和Java的for-each循環(huán)一致,其寫法和for<item> in <elements> 和C#一樣。
迭代數(shù)字:區(qū)間和數(shù)列
區(qū)間:兩個值之間的間隔,使用..來表示區(qū)間。
val oneToTen = 1..10
Kotlin的區(qū)間是閉合的,結束值是區(qū)間的一部分。
若你能迭代區(qū)間中所有的值,這樣的區(qū)間被稱作數(shù)列。
fun fizzBuzz(i: Int) = when {i % 15 == 0 -> "FizzBuzz "i % 3 == 0 -> "Fizz "i % 5 == 0 -> "Buzz "else -> "$i " }fun main(args: Array<String>) {for (i in 100 downTo 1 step 2) {print(fizzBuzz(i))} }上述代碼迭代了一個步長為2的遞減數(shù)列。
迭代map
下面的程序?qū)⒆址D換成二進制打印出來:
fun main(args: Array<String>) {val binaryReps = TreeMap<Char, String>()for (c in 'A'..'F') {val binary = Integer.toBinaryString(c.toInt())binaryReps[c] = binary}for ((letter, binary) in binaryReps) {println("$letter = $binary")} }..也可以創(chuàng)建字符區(qū)間。這里從A迭代到F,包括F。
for循環(huán)允許展開迭代中的集合的元素,并把展開的結果存儲到了兩個獨立變量letter和binary中。
可以用這樣的展開語法在迭代集合的同時跟蹤當前項的下標。
下面的代碼遍歷的list并打印出下標:
val list = arrayListOf("10", "11", "1101")for ((index, element) in list.withIndex()) {println("$index : $element")}使用“in”檢查集合和區(qū)間的成員
使用in運算符來檢查一個值是否在區(qū)間中,或者其逆運算,!n,檢查不在區(qū)間中。
下面的代碼檢查一個char是數(shù)字還是字母:
fun recognize(c: Char) = when (c) {in '0'..'9' -> "It's a digit!"in 'a'..'z', in 'A'..'Z' -> "It's a letter!"else -> "I don't know…?" }fun main(args: Array<String>) {println(recognize('8')) }區(qū)間不限于數(shù)字和字符,只要支持實例比較操作(實現(xiàn)了java.lang.Comparable接口),就能創(chuàng)建這一種類型的對象區(qū)間。
如果是這樣的區(qū)間,并不能列舉出這個區(qū)間中的所有對象。
比如我們不能列舉出“Java”和“Kotlin”之間所有的字符串。
但是我們?nèi)稳豢梢杂胕n運算符檢查一個對象是否屬于這個區(qū)間。
5.Kotlin中的異常
Kotlin的異常處理大體上和Java一致。Kotlin中的throw結構是一個表達式,能作為另一個表達式的一部分使用。
“tyr”“catch”和“finally”
下面的代碼從給定文件中讀取一行,解析成數(shù)字并返回值;若不是有效數(shù)字則返回null。
fun readNumber(reader: BufferedReader): Int? {try {val line = reader.readLine()return Integer.parseInt(line)}catch (e: NumberFormatException) {return null}finally {reader.close()} }fun main(args: Array<String>) {val reader = BufferedReader(StringReader("239"))println(readNumber(reader)) }和Java最大的區(qū)別是throws子句沒有出現(xiàn)在代碼中。
和其他許多現(xiàn)代JVM語言一樣,Kotlin并不區(qū)分受檢異常和未受檢異常。不用指定函數(shù)拋出的異常,可以處理也可以不處理異常。
“try”作為表達式
修改上面的例子如下:
fun readNumber(reader: BufferedReader) {val number = try {Integer.parseInt(reader.readLine())} catch (e: NumberFormatException) {return}println(number) }fun main(args: Array<String>) {val reader = BufferedReader(StringReader("not a number"))readNumber(reader) }try關鍵字引入了一個表達式,可以把它的值賦給一個變量。
return語句放在catch代碼塊中,因此該函數(shù)的執(zhí)行在catch代碼塊之后不會再繼續(xù)。
如果你需要返回值,可以將return替換為null,最后會打印出null
6.小結
- fun關鍵字用來聲明函數(shù),val和var分別聲明只讀變量和可變變量
- 字符串模板:在變量前加上$或者用${}包圍一個表達式,來把值注入字符串
- if,when,try catch都是帶返回值的表達式
- 檢查變量類型之后不必再顯示的轉換,編譯器用智能轉換自動幫你完成
- ..會創(chuàng)建一個區(qū)間,還可以使用in和!n運算符來檢查值是否屬于區(qū)間
- Kotlin不要求你聲明函數(shù)可以拋出的異常
總結
以上是生活随笔為你收集整理的《Kotlin实战》读书笔记第二章的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 利用百度云盘同步Zotero
- 下一篇: DFRobot新品Fermion:MCP