Kotlin系統(tǒng)化學(xué)習-20170823
文章中有大部分內(nèi)容是借鑒《Kotlin實戰(zhàn)》中文版判哥,特此注明一下。
Kotlin系統(tǒng)化系列學(xué)習文章(有待更新):
http://blog.csdn.net/ClAndEllen/article/details/77466628
前篇文章我們把Kotlin的背景管挟,學(xué)習流程整理了,以及詳細說明了Kotlin語言的一些優(yōu)勢弄捕,接下來進入正式的學(xué)習:
K02-Kotlin基礎(chǔ)
本篇文章學(xué)習內(nèi)容:
1.聲明函數(shù)僻孝,變量,類察藐,枚舉以及屬性皮璧。
2.Kotlin中的控制結(jié)構(gòu)。
3.智能轉(zhuǎn)換分飞。
4.拋出和處理異常悴务。
2.1 函數(shù)和變量
2.1.1 Hello,World!
fun main(args: Array<String>) {
println("Hello,World!")
}//相當于Java中的main,只不過這個main方法不放在一個單獨的類中,而是放于頂層
你能從這樣簡單的一小段代碼中觀察到哪些特性和語法譬猫?
1.關(guān)鍵字fun用來聲明一個函數(shù)讯檐。沒錯,Kotlin編程有很多樂趣(fun)染服。
2.參數(shù)類型寫在名稱后面别洪。變量的聲明也是如此。
3.函數(shù)可以定義在文件的最外層(頂層)柳刮,不需要把它放在類中挖垛。
4.使用println代替了System.out.println。Kotlin標準庫給Java標準庫函數(shù)提供了許多語法更簡潔的包裝秉颗,而println就是其中一個痢毒。
5.和許多其他現(xiàn)代語言一樣,可以省略每行代碼結(jié)尾的分號蚕甥。
2.1.2 函數(shù)
你已經(jīng)看到了怎樣聲明一個沒有任何返回任何東西的函數(shù)哪替。但是如果函數(shù)有一個有意義的結(jié)果,返回類型應(yīng)該放在哪里呢菇怀?其實很簡單就放置與參數(shù)列表之后凭舶,緊跟于參數(shù)列表之后,以“:”號隔開爱沟,示例代碼如下:
fun max(a:Int,b:Int):Int{
return if(a>=b) a else b
}//普通寫法
一個函數(shù)的基本結(jié)構(gòu)如下圖2.1所示:
語句和表達式:
在Kotlin中帅霜,if是表達式,而不是語句呼伸。語句和表達式的區(qū)別在于义屏,表達式有值,并且能作為另一個表達式的一部分使用;而語句總是包圍著它的代碼塊中的頂層元素闽铐,并且沒有自己的值蝶怔。在Java中,所有的控制結(jié)構(gòu)都是語句兄墅。而在Kotlin中踢星,除了循環(huán)(for,do和do/while)以外大多數(shù)控制結(jié)構(gòu)都是表達式。這種結(jié)合控制結(jié)構(gòu)和其他表達式的能力讓你可以簡明扼要地表示許多常見的模式隙咸,稍后你會在本書中看到這些內(nèi)容沐悦。
??另一方面,Java中的賦值操作是表達式五督,在Kotlin中反而變成了語句藏否。這有助于避免比較和賦值之間的混淆,而這種混淆是常見的錯誤來源充包。
表達式函數(shù)體
在Kotlin中副签,有一種函數(shù)的寫法,讓函數(shù)的聲明變得非常簡單基矮,因為它的函數(shù)體是單個表達式構(gòu)成淆储,這種寫法去掉了return和“{}”花括號,比如上面的代碼寫成表達式函數(shù)的形式如下:
fun max(a:Int,b:Int):Int = if(a>=b) a else b
如果函數(shù)體寫在花括號中家浇,我們說這個函數(shù)有代碼塊體本砰。如果它直接返回了一個表達式,它就有表達式體钢悲。上述代碼還可以這么簡化点额,把返回類型省略掉:
fun max(a:Int,b:Int) = if(a>=b) a else b
為什么有些函數(shù)可以不聲明返回類型?作為一門靜態(tài)類型語言莺琳,Kotlin不是要求每個表達式都應(yīng)該在編譯期具有類型嗎咖楣?事實上,每個變量和表達式都有類型芦昔。每個函數(shù)都有返回類型。但是對表達式函數(shù)來說娃肿,編譯器會分析作為函數(shù)體的表達式咕缎,并把它的類型作為函數(shù)的返回類型,即使沒有顯式地寫出來料扰。這種分析通常被稱作類型推導(dǎo)凭豪。
??注意,只有表達式體函數(shù)的返回類型可以省略晒杈。對于有返回值的代碼塊體函數(shù)嫂伞,必須顯式地寫出返回類型和return語句。這是刻意的選擇。真實項目中的函數(shù)一般很長且可以包含多條return語句帖努,顯式地寫出返回類型和return語句能幫助你快速地理解函數(shù)能返回的是什么撰豺。接下來我們看看聲明變量的語法。
2.1.3 變量
在Java中聲明變量的時候會以類型開始拼余。在Kotlin中這樣是行不通的污桦,因為許多變量聲明的類型都可以省略。所以在Kotlin中以關(guān)鍵字開始匙监,然后是變量名稱凡橱,最后可以加上類型(不加也可以):
val question =
"The Ultimate Question of Life,the Universe, and Everything"
val answer = 42
這個例子省略了類型聲明,但是如果需要也可以顯式地指定變量的類型:
val answer:Int = 42
和表達體函數(shù)一樣亭姥,如果你不指定變量的類型稼钩,編譯器會分析初始化器表達式的值,并把它的類型作為變量的類型达罗。在前面這個例子中坝撑,變量的初始化器42的類型是Int,那么變量就是這個類型。你可以理解編譯器很聰明氮块,總是給變量推導(dǎo)出合適的類型绍载。
??如果變量沒有初始化器,需要顯式地指定它的類型:如果不能提供可以可以賦給這個變量的值的信息滔蝉,編譯器就無法推斷出它的類型击儡。
可變量和不可變量
聲明變量的關(guān)鍵字有兩個:
val(來自value)---不可變引用。使用val聲明的變量不能在初始化之后再次賦值蝠引。它對應(yīng)的是Java的final的變量阳谍。
var(來自variable)---可變引用。這種變量的值可以被改變螃概。這種聲明對應(yīng)的是普通(非final)的Java變量楼眷。
默認情況下,應(yīng)該盡可能地使用val關(guān)鍵字來聲明所有的Kotlin變量蔚润,僅在必要的時候換成var灌危。使用不可變引用,不可變對象及無副作用的函數(shù)讓你的代碼更接近函數(shù)式編程風格冒窍。只要你對Java中的“final”理解夠透徹递沪,理解Kotlin中的val也就不在話下。我這里就不再啰嗦了综液。var雖然是可變的款慨,但是前提必須是類型匹配的,比如以下代碼:
var a = 3 //這里已經(jīng)確定a的類型為Int
a = 5
a = "3" //報錯谬莹,因為a的類型是不可變的
2.1.4 更簡單的字符串格式化:字符串模版
Java當中的字符串模版很死板檩奠,變量的輸出必須由"+"號連接桩了,卻不能鑲嵌于一個常規(guī)的String當中,比如輸出Student類的age和name,Java是這么輸出的:
System.out.println("學(xué)生姓名:"+s.name+"埠戳,學(xué)生年齡:"+s.age);
要是輸出Student類的屬性有很多井誉,那么就有點繁瑣了,Kotlin就避免這一麻煩乞而,可以在字符串字面值中引用局部變量送悔,只需要在變量名稱前面加上$,比如下面的代碼:
var a = 5
println("a的值是:$a")
當然如果要輸出一段表達式或者是引用調(diào)用某個屬性,函數(shù)等爪模,那么需要給輸出的區(qū)域添加上花括號欠啤,示例代碼如下:
fun main(args: Array<String>) {
var a1 = 5
var a2 = 6
val s = Student("ellen",23)
println("相加的的值是:${a1+a2}") // 輸出表達式
println("學(xué)生姓名:${s.name},學(xué)生年齡:${s.myName()}")//輸出通過引用調(diào)用屬性或者方法
}
class Student(name:String,age:Int){
var name = ""
var age = 0
fun myName() = name
}
現(xiàn)在你知道了如何定義函數(shù)和變量,那么接下來屋灌,來看看類洁段。這一次,你會借助Java到Kotlin的轉(zhuǎn)換器來開始運用新的語言特性共郭。
2.2 類和屬性
面向?qū)ο髮δ銇碚f可不是什么新鮮的概念祠丝,你也許非常熟悉類的抽象機制。Kotlin這方面的概念你也會覺得似曾相識除嘹,但是你會發(fā)現(xiàn)許多常見的任務(wù)使用更少的代碼就可以完成写半。這一節(jié)會向你介紹聲明類的基本語法,在后面在深入細節(jié)尉咕。
??首先叠蝇,來看一個簡單的JavaBean類Person,目前它只有一個屬性,name年缎。
Java代碼是這樣的:
private class Person{
private final String name;
public Person(String name){
this.name = name;
}
public String getName(){
return name;
}
}
在Java中悔捶,構(gòu)造方法的方法體常常包含完全重復(fù)的代碼:它把參數(shù)賦值給有著相同名稱的字段。在Kotlin中单芜,這種邏輯不用這么多的樣板代碼就可以表達蜕该。后面介紹Java到Kotlin的轉(zhuǎn)換器:一個把Java代碼替換成功能相同的Kotlin代碼的工具。把上述代碼使用轉(zhuǎn)換器轉(zhuǎn)換的代碼如下:
class Person(val name:String)
就是這么強悍洲鸠,如果你試過其他的一些現(xiàn)代JVM語言堂淡,你也許見過類似的事情。這中類(只有數(shù)據(jù)沒有其他代碼)通常被叫作值對象扒腕,許多語言都提供簡明語法來聲明它們绢淀。
??注意從Java到Kotlin的轉(zhuǎn)換過程中public修飾符消失了。在Kotlin中public是默認的可見性袜匿,所以你能省略它。
2.2.1 屬性
你肯定知道稚疹,類的概念就是把數(shù)據(jù)和處理數(shù)據(jù)的代碼封裝成一個單一的實體居灯。在Java中祭务,數(shù)據(jù)存儲在字段中,通常還是私有的怪嫌。如果想讓類的使用者訪問到數(shù)據(jù)义锥,得提供訪問器方法:一個getter,可能還有一個setter。在Person類中你已經(jīng)看到了訪問器的例子岩灭。setter還可以包含額外的邏輯拌倍,包括驗證傳給它的值。發(fā)送關(guān)于變化的通知等噪径。
??在Java中柱恤,字段和其訪問器的組合常常被叫作屬性,而許多框架嚴重依賴這個概念找爱。在Kotlin中梗顺,屬性是頭等的語言特性,完全代替了字段和訪問器方法车摄。在類中聲明一個屬性和聲明一個變量一樣:使用val和var關(guān)鍵字寺谤。聲明成val的屬性是只讀的,而var屬性是可變的吮播。
class Person{
val name:String, //只讀屬性:生成一個字段和一個簡單的getter
var isMarried:Boolean //可見屬性:一個字段变屁,一個getter和一個setter
}
基本上,當你聲明屬性的時候,你就聲明了對應(yīng)的訪問器(只讀屬性只有一個getter意狠,而可寫屬性既有g(shù)etter粟关,也有setter)。訪問器的默認實現(xiàn)非常簡單:創(chuàng)建一個存儲值的字段摄职,以及返回值的getter和更新值的setter誊役。但是如果有需要,可以聲明自定義的訪問器谷市,使用不同的邏輯來計算和更新屬性的值蛔垢。
Person person = new Person("ellen",false);
System.out.println(person.getName());
System.out.println(person.isMarried());
注意,不管Person是定義在Java還是Kotlin中迫悠,這段代碼看起來是一樣的鹏漆。Kotlin的屬性name把一個名稱getName的getter方法暴露給Java。getter和setter的命名規(guī)則有一個例外:如果屬性的名稱以is開頭创泄,getter不會增加任何的前綴艺玲;而它的setter名稱中的is會被替換成set。所以在Java中鞠抑,你強調(diào)的將是isMarried()饭聚。
??將上述Java代碼使用Person的代碼換成Kotlin代碼如下:
val person = Person("ellen",true) //調(diào)用構(gòu)造方法不需要關(guān)鍵字“new”
//可以直接訪問屬性,但是調(diào)用的是getter
println(person.name)
println(person.isMarried())
現(xiàn)在搁拙,可以直接引用屬性秒梳,不再需要調(diào)用getter法绵。邏輯沒有變化,但代碼更簡潔了酪碘∨笃可變屬性的setter也是這樣:在Java中,使用person.setMarried(false)來表示離婚兴垦,而在Kotlin中徙赢,可以這樣寫person.isMarried = false 。
小貼士 對于那些在Java中定義的類探越,一樣可以使用Kotlin的屬性語法狡赐。Java類中的getter可以被當成val屬性在Kotlin中訪問,而一對getter/setter可以被var屬性訪問扶关。例如阴汇,如果一個Java類定義了兩個名稱為getName和setName的方法,就把它們當作名稱為name的屬性訪問节槐。如果類定義了isMarried和setMarried方法搀庶,對應(yīng)的Kotlin屬性的就是isMarried。
大多數(shù)情況下铜异,屬性有一個對應(yīng)的支持字段來保存屬性的值哥倔。但是如果這個值可以即時計算---例如,根據(jù)其他屬性計算---可以自定義的getter來表示揍庄。
2.2.2 自定義訪問器
這一節(jié)將向你展示怎樣寫一個屬性訪問器的自定義實現(xiàn)咆蒿。假設(shè)你聲明這樣一個矩形。它能判斷自己是否是正方形蚂子。不需要一個單獨的字段來存儲這個信息(是否是正方形)沃测,因為可以隨時通過檢查矩形的長寬是否相等來判斷:
class Rectangle(val height:Int,val width:Int){
val isSquare:Boolean
get(){
return height == width
}
}
屬性isSquare不需要字段來保存它的值。它只有一個自定義實現(xiàn)的getter食茎。它的值時每次訪問屬性的時候計算出來的蒂破。
??注意,不需要使用帶花括號的完整語法别渔,也可以這樣寫get( ) = height == width附迷。對這個屬性的調(diào)用依然不變。
val rectangle = Rectangle(41,43)
println(rectangle,isSquare)//輸出false
如果在Java中訪問這個屬性哎媚,可以像前面提到的那樣調(diào)用isSquare()的方法喇伯。
??你可能會問,聲明一個沒有參數(shù)的函數(shù)是否比聲明帶自定義getter的屬性更好拨与。兩種方式幾乎一樣:實現(xiàn)和性能都沒有差別稻据,唯一的差異時可讀性。通常來說买喧,如果描述的是類的特征(屬性)捻悯,應(yīng)該把它聲明成屬性箩朴。接下來我們來探索一下Kotlin的代碼在磁盤中說怎樣組織的。
2.2.3 Kotlin源碼布局:目錄和包
你知道Java把所有的類組織成包秋度。Kotlin也有和Java相似的包的概念。每一個Kotlin文件都能以一條package語句開頭钱床,而文件中定義的所有聲明(類荚斯,函數(shù)及屬性)都會放在這個包中。如果其他文件中定義的聲明也有相同的包查牌,這個文件可以直接使用他們事期;如果包不相同,則需要導(dǎo)入它們纸颜。和Java一樣兽泣,導(dǎo)入語句放在文件的最前面并使用關(guān)鍵字import。下面這個源碼文件的例子展示了包聲明和導(dǎo)入語句的語法胁孙。
package geometry.shapes
import java.util.Random
class Rectangle(val height:Int,val width:Int){
val isSquare:Boolean
get() = height == width
}
fun createRandomRectangle():Rectangle{
val random = Random()
return Rectangle(random.nexInt(),random.nexInt())
}
Kotlin不區(qū)分導(dǎo)入的是類還是函數(shù)唠倦,而且,它允許使用import關(guān)鍵字導(dǎo)入任何種類的聲明涮较〕肀牵可以直接導(dǎo)入頂層函數(shù)的名稱。
package geometry.example
import geometry.shapes.createRandomRectangle
fun main(args:Array<String>){
println(createRandomRectangle().isSquare)
}
也可以在包名稱后加上.來導(dǎo)入特定包中定義的所有聲明狂票。注意這種星號導(dǎo)入不僅讓包中定義的類可見候齿,也會讓頂層函數(shù)和屬性可見。在上述代碼中用import geometry.shapes.的寫法代替顯式的導(dǎo)入也能讓代碼成功編譯闺属。
??在Java中慌盯,要把類放到和包結(jié)構(gòu)相匹配的文件與目錄結(jié)構(gòu)中。例如掂器,如果你有一個包含若干類的名為shapes的包亚皂,必須把每一個類都放在一個有著和類相同名字的單獨文件中,然后把這些文件放在一個名字為shapes的目錄中唉匾。圖2.2展示了geometry包以及它的子包在Java中上是怎樣組織的孕讳。假設(shè)createRandomRectangle函數(shù)位于另外一個單獨的類RectangleUtil。
在Kotlin中厂财,可以把多個類放在同一個文件中,文件的名字還可以隨意選擇峡懈。Kotlin也沒有對磁盤上源文件的布局強加任何限制璃饱。比如,可以把包geometry.shapes所有內(nèi)容都放在文件shapes.kt中肪康,并把這個文件直接放在目錄geometry中荚恶,而不需要在創(chuàng)建一個獨立的shapes文件夾(如下圖所示)
example.kt --> 包geometry.example
shapes.kt --> 包geometry.example
不管怎樣撩穿,大多數(shù)情況下,遵循Java的目錄布局并根據(jù)包結(jié)構(gòu)把源碼文件放到目錄中谒撼,依然是個不錯的實踐食寡。在Kotlin和Java混用的項目中堅持這樣的結(jié)構(gòu)尤為重要,因為這樣做可以讓你逐步地遷移代碼廓潜,而不會和一些錯誤不期而遇抵皱。但是你應(yīng)該毫不猶豫地把多個類放進同一個文件中,特別是那些很小的類(在Kotlin中辩蛋,類通常很猩牖)。接下來悼院,來學(xué)習Kotlin控制結(jié)構(gòu)伤为。
2.3 表示和處理選擇:枚舉和“when”
2.3.1 聲明枚舉類
Kotlin中的枚舉類聲明說和Java是差不多的,下面我們就來看看Kotlin中的枚舉据途,下面來看看使用Kotlin來進行色彩枚舉類的聲明:
enum class Color{
RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOET
}
這是極少數(shù)Kotlin聲明比Java使用了更多關(guān)鍵字的例子:Kotlin用了enum class兩個關(guān)鍵字绞愚,而Java只有enum一個關(guān)鍵字。Kotlin中颖医,enum是一個所謂的軟關(guān)鍵字:只有當它出現(xiàn)在class前面才有特殊的意義爽醋,在其他地方可以把它當作普通的名稱使用。與此不同的是便脊,class仍然是一個關(guān)鍵字蚂四,要繼續(xù)使用名稱clazz和aClass來聲明變量。
??和Java一樣哪痰,枚舉并不是值的列表:可以給枚舉聲明屬性和方法遂赠。下面的代碼清單展示了這種方式。
fun main(args: Array<String>) {
println(Color.BLUE.rgb()) //輸出:255
}
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
}
枚舉常量用的聲明構(gòu)造方法和屬性的語法與之前你看到的常規(guī)類一樣晌杰。當你聲明每個枚舉常量的時候跷睦,必須提供該變量的屬性值。注意這個例子向你展示Kotlin語法中唯一必須使用分號的地方:如果要在枚舉類中定義任何方法肋演,就要使用分號把枚舉常量列表和方法定義分開∫种睿現(xiàn)在我們來看看一些在代碼中處理枚舉常量的超酷的方式。
2.3.2 使用“when”處理枚舉類
和if相似爹殊,when是一個有返回值的表達式蜕乡,因此可以寫一個直接返回when表達式的表達式體函數(shù)。
fun main(args: Array<String>) {
println(getColorString(Color.INDIGO)) //輸出:INDIGO
}
fun getColorString(color:Color) = when(color){//Java中的switch在Kotlin中使用when表達式實現(xiàn)
Color.RED->"RED"
Color.ORANGE->"ORANGE"
Color.YELLOW->"YELLOW"
Color.GREEN->"GREEN"
Color.BLUE->"BLUE"
Color.INDIGO->"INDIGO"
Color.VIOLET->"VIOLET"
}//非合并when分支
fun getColorString1(color:Color) = when(color){
Color.RED, Color.ORANGE->"RED OR ORANGE"http://合并分支
Color.YELLOW, Color.GREEN, Color.BLUE->"YELLOW GREEN BLUE"http://合并分支
Color.INDIGO->"INDIGO"
Color.VIOLET->"VIOLET"
}
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
}
如果你還覺得上面代碼when表達式不夠簡潔梗夸,那么你可以通過導(dǎo)入枚舉常量的方式來簡化when表達式:
import geometry.Color
import geometry.Color.*
fun main(args: Array<String>) {
println(getColorString(BLUE))
}
fun getColorString(color:Color) = when(color){
RED->"RED"
ORANGE->"ORANGE"
YELLOW->"YELLOW"
GREEN->"GREEN"
BLUE->"BLUE"
INDIGO->"INDIGO"
VIOLET->"VIOLET"
}
2.3.3 在“when”結(jié)構(gòu)中使用任意對象
Kotlin中的when結(jié)構(gòu)比Java中的Switch強大很多层玲。Switch要求必須使用常量(枚舉常量,字符串或者數(shù)字字面值)作為分支條件,和它不一樣辛块,when允許使用任意對象畔派。我們寫一個函數(shù)來混合兩種顏色,如果它們在我們這個小小的調(diào)色板是能夠混合的润绵。你只有很少的選項线椰,可以簡單地把所有組合列舉出來。
fun mix(c1:Color,c2:Color){
when(setOf(c1,c2)){//setof()方法返回一個set集合
setOf(RED,YELLOW)->ORANGE
setOf(YELLOW,BLUE)->GREEN
setOf(BLUE,VIOLET)->INDIGO
else->throw Exception("Dirty color")//如果沒匹配其它分支尘盼,就會執(zhí)行此處
}
}
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
}
when表達式把它的實參依次和所有分支匹配士嚎,直到某個分支滿足條件。這里setOf(c1,c2)被用來檢查是否和分支條件相等:先和setOf(RED,YELLOW)比較悔叽,然后是其他顏色的set,一個接一個爵嗅。如果沒有其他的分支滿足條件娇澎,else分支會執(zhí)行。
??能使用任何表達式做when的分支條件睹晒,很多情況下會讓你寫的代碼既簡潔又漂亮趟庄。這個例子中,分支條件是等式檢查伪很,接下來你會看到條件還可以是任意的布爾表達式戚啥。
2.3.4 使用不帶參數(shù)的“when”
你可能注意到上述代碼的效率多少有些低。每次調(diào)用這個函數(shù)的時候锉试,它都會創(chuàng)建一些Set實例,僅僅用來檢查兩種給定的顏色是否和另外兩種顏色匹配猫十。一般這不是什么大問題,但是如果這個函數(shù)調(diào)用很頻繁呆盖,它就會非常值得用另外一種方式重寫拖云,來避免創(chuàng)建額外的垃圾對象。代碼可讀性會變差应又,但這是為了達到更好性能而必須付出的代價宙项。
fun mix(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) -> INDIGO
else -> throw Exception("Dirty color")
}
}
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
}
如果沒有給when表達式提供參數(shù),分支條件就是任意的布爾表達式株扛。mixOptimized函數(shù)和前面的mix函數(shù)做的事情一樣尤筐。這種寫法的有點就是不會創(chuàng)建額外的對象,但代價是它更難理解洞就。
2.3.5 智能轉(zhuǎn)換:合并類型檢查和轉(zhuǎn)換
你會寫一個函數(shù)來作為這一小節(jié)的例子盆繁,這個函數(shù)是對象(1+2)+4這樣簡單的算術(shù)表達式求值。這個表達式只包含一種運算:對兩個數(shù)字求和旬蟋。其他的算術(shù)運算(減法改基,乘法,除法)都可以用相似的方式實現(xiàn),可以把這些作為練習秕狰。
??首先稠腊,你會用怎樣的形式編碼這種表達式?把它們存儲在一個樹狀結(jié)構(gòu)中鸣哀,結(jié)構(gòu)中每個節(jié)點要么是一次求和(Sum)要么是一個數(shù)字(Num)架忌。Num永遠都是葉子節(jié)點,而Sum節(jié)點有兩個子節(jié)點:它們是求和運算的兩個參數(shù)我衬。下面的代碼展示了一種簡單的類結(jié)構(gòu)來表示這種表達式編碼方式:一個叫作Expr的接口和它的兩個實現(xiàn)類Num和Sum叹放。注意Expr接口沒有聲明任何方法,它只是一個標記接口挠羔,用來給不同種類的表達式提供一個公共的類型井仰。聲明類的時候,使用一個冒號(:)后面跟上接口名稱破加,來標記這個類實現(xiàn)了這個接口俱恶。
interface Expr
class Num(var value:Int):Expr //簡單的值對象類,只有一個屬性value,實現(xiàn)了Expr接口
class Sum(val left:Expr,val right:Expr):Expr//Sum運算的實參可以是任何Expr:Num或者另外一個Sum
Sum存儲了Expr類型的實參left和right的引用范舀;在這個小例子中合是,它們要么是Num要么是Sum。為了存儲前面提到的表達式(1+2)+4,你會創(chuàng)建這樣一個對象Sum(Sum(Num(1),Num(2)),Num(4))锭环。下圖展示了它的樹狀圖表示法聪全。
現(xiàn)在我們來看看怎樣計算表達式的值。例子中表達式的運算結(jié)果應(yīng)該是7辅辩。
Expr接口有兩種實現(xiàn)难礼,所以為了計算出表達式的結(jié)果值,得嘗試兩種選項:
??1.如果表達式是一個數(shù)字玫锋,直接返回它的值鹤竭。
??2.如果是一次求和,得先計算左右兩個表達式得值景醇。再返回它們的和臀稚。
首先我們來看看這個函數(shù)用普通的Java方式怎么寫,然后我們把它重構(gòu)成Kotlin風格寫法三痰。在Java中吧寺,很可能會用一連串if語句來檢查這些選項,所以我們先用Kotlin按照這種方式實現(xiàn):
interface Expr
class Num(var value:Int):Expr //簡單的值對象類散劫,只有一個屬性value,實現(xiàn)了Expr接口
class Sum(val left:Expr,val right:Expr):Expr//Sum運算的實參可以是任何Expr:Num或者另外一個Sum
fun eval(e:Expr):Int{
if(e is Num){
val n = e as Num
return n.value
}
if(e is Sum){
return eval(e.left)+ eval(e.right)
}
throw IllegalArgumentException("Unknown expression")
}
在Kotlin中稚机,你要使用is檢查來判斷一個變量是否是某種類型。如果你曾經(jīng)使用過C#寫過代碼获搏,這種表示法應(yīng)該不會陌生赖条。is檢查和Java中的instanceof相似。但是Java中,如果你已經(jīng)檢查過一個變量是某種類型并且把它當作這種類型來訪問其成員時纬乍,在instanceof檢查之后還需要顯式地加上類型轉(zhuǎn)換碱茁。如果最初的變量會使用超過一次,常常選擇把類型轉(zhuǎn)換的結(jié)果存儲在另外一個單獨的變量里仿贬。在Kotlin中纽竣,編譯器幫你完成這些工作。如果你檢查過一個變量是某種類型茧泪,后面就不需要再轉(zhuǎn)換它蜓氨,可以就把它當作你檢查過的類型使用。事實上編譯器為你執(zhí)行了類型轉(zhuǎn)換队伟,我們把這種行為稱為智能轉(zhuǎn)換穴吹。
??在eval函數(shù)中,在你檢查過變量e是否為Num類型之后嗜侮,編譯器就把它當成Num類型的變量解釋港令。于是你不需要進行顯式轉(zhuǎn)換就可以像這樣訪問Num的屬性value:e.value。Sum的屬性left和right也是這樣:在對應(yīng)的上下文中棘钞,只需要寫e.left和e.right。在IDE中干毅,這種智能轉(zhuǎn)換過的值會用不同的背景顏色著重表示宜猜,這樣更容易發(fā)現(xiàn)這個值的類型是事先檢查過的,如下圖所示:
智能轉(zhuǎn)換只在變量經(jīng)過is檢查之后不再發(fā)生變化的情況下有效硝逢,當你對一個類的屬性進行智能轉(zhuǎn)換的時候姨拥,就像這個例子中的一樣,這個屬性必須是一個val屬性渠鸽,而且不能有自定義的訪問器叫乌。否則,每次對屬性的訪問是否都能返回同樣的值將無從驗證徽缚。使用as關(guān)鍵字來表示特定類型的顯式轉(zhuǎn)換憨奸。
接下來看看怎樣把eval函數(shù)重構(gòu)成更符合Kotlin語言習慣的風格。
2.3.6 重構(gòu):用“when”代替“if”
Kotlin和Java中的if有什么不同凿试,你已經(jīng)看到了排宰。本章開始的時候,你見過if表達式用在適用Java三元運算符的上下文:if(a>b)a else b和 a > b ? a : b效果一樣那婉。Kotlin沒有三元運算符板甘,因為if表達式有返回值,這一點和Java不同详炬。這意味著你可以用表達式體語法重寫eval函數(shù)盐类,去掉return語句和花括號,使用if表達式作為函數(shù)體。
interface Expr
class Num(var value:Int):Expr //簡單的值對象類在跳,只有一個屬性value,實現(xiàn)了Expr接口
class Sum(val left:Expr,val right:Expr):Expr//Sum運算的實參可以是任何Expr:Num或者另外一個Sum
fun eval(e:Expr) : Int =
if(e is Num){
e.value
} else if(e is Sum){
eval(e.left) + eval(e.right)
}else{
throw IllegalArgumentException("Unknown expression")
}
如果if分支中只有一個表達式枪萄,花括號是可以省略的。如果if分支是一個代碼塊硬毕,代碼塊中的最后一個表達式會被作為結(jié)果返回呻引。
??讓我們進一步打磨代碼,使用when來重寫它吐咳。
interface Expr
class Num(var value:Int):Expr //簡單的值對象類逻悠,只有一個屬性value,實現(xiàn)了Expr接口
class Sum(val left:Expr,val right:Expr):Expr//Sum運算的實參可以是任何Expr:Num或者另外一個Sum
fun eval(e:Expr) : Int =
when(e){
is Num ->
e.value
is Sum ->
eval(e.left) + eval(e.right)
else ->
throw IllegalArgumentException("Unknown expression")
}
when表達式并不僅限于檢查值是否相等,那是之前你看到的韭脊。而這里使用了另外一種when分支的形式童谒,允許你檢查when實參值的類型。和上述if的例子一樣沪羔,類型檢查應(yīng)用了一次智能轉(zhuǎn)換饥伊,所以不需要額外的轉(zhuǎn)換就可以訪問Num和Sum的成員。
??比較最后兩個Kotlin版本的eval函數(shù)蔫饰,想一想你應(yīng)該怎樣在自己的代碼中也使用when代替連串的if表達式琅豆。當分支邏輯太過復(fù)雜時,可以使用代碼塊作為分支體篓吁。我們來看看這種用法茫因。
2.3.7 代碼塊作為“if”和“when”的分支
if和when都可以使用代碼塊作為分支體。這種情況下杖剪,代碼塊中的最后一個表達式就是結(jié)果冻押。如果在例子函數(shù)中加入日志,可以在代碼塊中實現(xiàn)它并像之前一樣返回最后的值盛嘿。
interface Expr
class Num(var value:Int):Expr //簡單的值對象類洛巢,只有一個屬性value,實現(xiàn)了Expr接口
class Sum(val left:Expr,val right:Expr):Expr//Sum運算的實參可以是任何Expr:Num或者另外一個Sum
fun main(args: Array<String>) {
println(evalWithLogging(Sum(Sum(Num(1),Num(2)),Num(4))))
}
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")
}
輸出結(jié)果:
num:1
num:2
Sum:1 + 2
num:4
Sum:3 + 4
7
規(guī)則————“代碼中最后的表達式就是結(jié)果”,在所有使用代碼塊并期望得到一個結(jié)果的地方成立次兆。你會在文章末尾看到稿茉,同樣的規(guī)則對try主體和catch子句也有效,而文章K05還會討論該規(guī)則在lambda表達式中的應(yīng)用芥炭。但是在2.2中我們提到狈邑。這個規(guī)則對常規(guī)函數(shù)不成立。一個函數(shù)要么具有不是代碼塊的表達式函數(shù)體蚤认,要么具有包含顯式return語句的代碼塊函數(shù)體米苹。
??現(xiàn)在熟悉了Kotlin從眾多選項中做出正確選擇的方式,是時候看看怎樣迭代事物了砰琢。
2.4 迭代事物:“while”循環(huán)和“for”語句
在本章討論的所有特性中蘸嘶,Kotlin的迭代應(yīng)該是和Java最接近的良瞧。when循環(huán)和Java完全一樣,本節(jié)開頭會一筆帶過训唱。for循環(huán)僅以唯一一種形式存在褥蚯,和Java的for-each循環(huán)一致羡藐。其寫法for <item> in
<elements>和C#一樣谎替。和Java一樣语卤,循環(huán)最常見的應(yīng)用就是迭代集合谁鳍。我們也會探索它是怎樣覆蓋其他使用循環(huán)的場景的。
2.4.1 “while”循環(huán)
Kotlin有while循環(huán)和do-while循環(huán)醋闭,它們的語法和Java相應(yīng)的循環(huán)沒有什么區(qū)別:
while(condition){
/*...*/
}//先判斷哪廓,后執(zhí)行
do{
/*...*/
} while(condition)//先執(zhí)行沙热,后判斷
Kotlin并沒有給這些簡單的循環(huán)帶來任何新東西为肮,所以不必停留摊册。我們繼續(xù)討論for循環(huán)的各種用法。
2.4.2 迭代數(shù)字:區(qū)間和數(shù)列
正如我們剛剛提到的那樣颊艳,在Kotlin中沒有常規(guī)的Java for循環(huán)茅特。在這種循環(huán)中,先初始化變量棋枕,在循環(huán)的每一步更新它的值白修,并在值滿足某個限制條件時退出循環(huán)。為了替代這種最常見的循環(huán)用法重斑,Kotlin使用了區(qū)間的概念兵睛。
??區(qū)間本質(zhì)上就是兩個值之間的間隔,這兩個值通常是數(shù)字:一個起始值绸狐,一個結(jié)束值卤恳。使用..運算符來表示區(qū)間:
var oneToTen = 1..10
注意Kotlin的區(qū)間是包含的或者閉合的累盗,意味著第二個值始終是區(qū)間的一部分寒矿。
??你能用整數(shù)區(qū)間做的最基本的事情就是循環(huán)迭代其中所有的值。如果你能迭代區(qū)間中所有的值若债,這樣的區(qū)間被稱作數(shù)列符相。
??讓我們用整數(shù)迭代來玩Fizz-Buzz游戲。這是一種用來打發(fā)長途駕駛旅程的不錯方式蠢琳,還能幫你回憶起被遺忘的除法技巧啊终。游戲玩家輪流遞增計數(shù),遇到能被3整數(shù)的數(shù)字就用單詞fizz代替傲须,遇到能被5整除的數(shù)字則用單詞buzz代替蓝牲。如果一個數(shù)字3和5的公倍數(shù),你得說出“FizzBuzz”泰讽。
??下面的代碼打印出了游戲中1到100之間所有的數(shù)字的正確答案例衍。注意你是怎樣用不帶參數(shù)的when表達式來檢查可能的條件的昔期。
fun main(args: Array<String>) {
for(i in 1..100){
println(fizzBuzz(i))
}
}
fun fizzBuzz(i:Int) = when{
i % 15 == 0 ->"FizzBuzz"
i % 3 == 0 ->"Fizz"
i % 5 == 0 ->"Buzz"
else -> "$i"
}
假設(shè)一個小時的駕駛之后,你已經(jīng)厭倦了這些規(guī)則佛玄,想把游戲變得復(fù)雜一點硼一,那我們可以從100開始倒著技術(shù)并且只計偶數(shù)。
??現(xiàn)在你在迭代一個帶步長的數(shù)列梦抢,它允許跳過一些數(shù)字般贼。步長也可以是負數(shù),這種情況下數(shù)列是遞減而不是遞增的奥吩。在這個例子中哼蛆,100 downTo 1是遞減的數(shù)列(步長為-1)。然后step把步長的絕對值變成了2圈驼,但方向保持不變(事實上人芽,步長被設(shè)置成了-2)。
??如前所述绩脆,..語法始終創(chuàng)建的是包含結(jié)束值(..右邊的值)的區(qū)間萤厅。許多情況下,迭代不包括指定結(jié)束值的半閉合區(qū)間更方便靴迫。使用until函數(shù)可以創(chuàng)建這樣的區(qū)間惕味。例如,循環(huán)for(x in 0 until size)雖然等同于for(x in 0..size-1)玉锌,但是更清晰地表達了意圖名挥。在之后的文章中,你會學(xué)習更多關(guān)于這些例子中downTo,step和until的語法主守。
??可以看到使用區(qū)間和數(shù)列是怎樣幫助你應(yīng)付FizzBuzz游戲的進階規(guī)則的≠骶螅現(xiàn)在讓我們看看其他使用for循環(huán)的例子。
2.4.3 迭代map
我們提到了使用for...in循環(huán)的最常見的場景迭代集合参淫。這和Java中的用法一樣救湖,所以我們不會講太多關(guān)于它的內(nèi)容。讓我們來看看你可以怎樣迭代map涎才。
??作為例子鞋既,我們看看這個打印字符二進制表示的小程序。你會把這些二進制表示保存在一個map中(僅說明之用)耍铜。下面的代碼創(chuàng)建了一個map,把某些字母的二進制填充進去邑闺,最后打印map的內(nèi)容。
fun main(args: Array<String>) {
val binaryReps = TreeMap<Char,String>() //使用TreeMap讓鍵排序
for(c in 'A'..'F'){ //使用字符區(qū)間迭代從A到F之間的字符
val binary = Integer.toBinaryString(c.toInt())
binaryReps[c] = binary //根據(jù)c把值存儲到map中
}
for((letter,binary) in binaryReps){//迭代map,把鍵和值賦給兩個變量
println("$letter = $binary")
}
}
..語法不僅可以創(chuàng)建數(shù)字區(qū)間棕兼,還可以創(chuàng)建字符區(qū)間陡舅。這里使用它迭代從A開始到F的所有字符,包括F伴挚。
??展示了for循環(huán)允許展開迭代中的集合的元素(在這個例子中靶衍,展開的是map的鍵值對集合)臂寝。把展開的結(jié)果存儲到了兩個獨立的變量中:letter是鍵,binary是值摊灭。稍后咆贬,你將學(xué)到更多的展開語法。
??上述代碼中使用了一個使用的小技巧帚呼,根據(jù)鍵來訪問和更新map的簡明語法掏缎。可以使用map[key]
讀取值煤杀,并使用map[key] = value設(shè)置它們眷蜈,而不需要調(diào)用get和put。下面這段代碼
binaryReps[c] = binary//等價于Java版本代碼“binaryReps.put(c,binary);”
可以用這樣的展開語法在迭代集合的同時跟蹤當前項的下標沈自。不需要創(chuàng)建一個單獨的變量來存儲下標并手動增加它:
val list = arrayListOf("10","11","1001")
for((index,element) in list.withIndex()){ //迭代集合時使用下標
println("$index:$element")
}
之后的文章我們會探索關(guān)于withIndex的內(nèi)容酌儒。
??我能已經(jīng)看過了如何使用關(guān)鍵字in來迭代區(qū)間或者集合,還可以用in來檢查區(qū)間或者集合是否包含了某個值枯途。
2.4.4 使用“in”檢查集合和區(qū)間的成員
使用in運算來檢查一個值是否在區(qū)間中忌怎,或者它的逆運算,!in,來檢查這個值是否不在區(qū)間中酪夷。下面展示了如何使用in來檢查一個字符是否屬于一個字符區(qū)間榴啸。
//使用in來檢查一個字符是否屬于英文字母
fun isLetter(c:Char) = c in 'a'..'z'||c in 'A'..'Z' //方式1
fun isNotDigit(c:Char) = c !in '0'..'9' //方式2
這種檢查字符是否是英文字母的技巧看起來很簡單。在底層晚岭,沒有什么特殊處理:你依然會檢查字符的編碼是否位于第一個字母編碼和最后一個字母編碼之間的某個位置鸥印。但是這個邏輯被簡潔地隱藏到了標準庫中的區(qū)間類實現(xiàn)中:
c in 'a'..'z' //變換成'a'<=c&&c<='z'
in運算符和!in運算符也適用于when表達式坦报。
fun recognize(c : Char) = when(c){
in '0'..'9' -> "c是數(shù)字字符"
in 'a'..'z','A'..'Z' -> "c是英文字符"
else -> "不知道c是啥字符库说,反正不是數(shù)字字符和英文字符"
}
區(qū)間也不僅限于字符。假如有一個支持實例比較操作的任意類(實現(xiàn)了java.lang.Comparable接口)片择,就能創(chuàng)建這種類型的對象的區(qū)間潜的。如果這樣的區(qū)間,并不能列舉出這個區(qū)間種的所有的對象构回。想想這種情況:例如夏块,是否可以列舉出“Java”和“Kotlin”之間所有的字符串疏咐?答案是不能纤掸。但是仍然可以使用in運算符檢查一個其他的對象是否屬于這個區(qū)間:
println("Kotlin" in "Java".."Scala") //結(jié)果和"Java"<="Kotlin" && "Kotlin"<="Scala"一樣
注意,這里字符串是按照字母排序進行比較的浑塞,因為String就是這樣實現(xiàn)Comparable接口的借跪。
??in檢查也同樣適用于集合:
println("Kotlin" in setOf("Java","Scala")) //因為Set集合不包含“Kotlin”,輸出false
在之后的文章種我們將會對我們自己的數(shù)據(jù)類型的區(qū)間和數(shù)列使用in檢查學(xué)習。下面來看看Kotlin種的異常酌壕。
2.5 Kot中的異常
Kotlin中的異常處理和Java以及其他語言的處理方式相似掏愁。一個函數(shù)可以正常結(jié)束歇由,也可以出現(xiàn)錯誤的情況下拋出異常。方法的調(diào)用者能捕獲這個異常并處理它果港;如果沒有被處理沦泌,異常會沿著調(diào)用棧再次拋出。
??Kotlin中異常處理語句的基本形式和Java類似辛掠,拋出異常的方式也不例外:
if(percentage !in 0..100){
throw IllegalArgumentException(
"A percentage value must be between 0 and 100:$percentage")
}
和所有其他類一樣谢谦,不必使用new關(guān)鍵字來創(chuàng)建異常實例。
??和Java不同的是萝衩,Kotlin中throw結(jié)構(gòu)是一個表達式回挽,能作為另一個表達式的一部分使用:
val percentage =
if(number in 0..100)
number
else
throw IllegalArgumentException(
"A percentage value must be between 0 and 100:$number")//throw是一個表達式
在這個例子中,如果條件滿足猩谊,程序的行為是正確的千劈,而percentage變量會用number初始化。否則牌捷,異常將會拋出墙牌,而變量也不會初始化。在之后的文章中暗甥,我們將會討論討論關(guān)于throw作為其他表達式的一部分的技術(shù)細節(jié)憔古。
2.5.1 “try” “catch” 和 “finally”
和Java一樣,使用帶有catch和finally子句的try結(jié)構(gòu)來處理異常淋袖。你會在下面這個代碼中看到這個結(jié)構(gòu)鸿市,這個例子從給定的文件中讀取一行,嘗試把它解析成一個數(shù)字即碗,返回這個數(shù)字焰情;或者當這一行不是一個有效數(shù)字時返回null。
fun readNumber(reader:BufferedReader):Int?{
try{
val line = read.readLine()
return Integer.parseInt(line)
}catch(e : NumberFormatException){
return null
}finally{
reader.close()
}
}
和Java最大的區(qū)別就是throws子句沒有出現(xiàn)在代碼中:如果用Java來寫這個函數(shù)剥懒,你會顯式地在函數(shù)聲明后寫上throws IOException内舟。你需要這樣做的原因是IOException是一個受檢異常。在Java中初橘,這種異常必須顯式地處理验游。必須聲明你的函數(shù)能拋出的所有受檢異常。如果你調(diào)用另外一個函數(shù)保檐,需要處理這個函數(shù)的受檢異常耕蝉,或者聲明你的函數(shù)也能拋出這些異常。
??和其他許多現(xiàn)代的JVM語言一樣夜只,Kotlin并不區(qū)分受檢異常和未受檢異常垒在。不用指定函數(shù)拋出的異常,而且可以處理也可以不處理異常扔亥。這種設(shè)計是基于Java中使用異常的實踐做出的決定场躯。經(jīng)驗顯示這些Java規(guī)則常常導(dǎo)致許多毫無意義的重新拋出或者忽略異常的代碼谈为,而且這些規(guī)則不能總是保護你免受可能發(fā)送的錯誤。
??在上述代碼中踢关,NumberFormatException就不是受檢異常伞鲫。因此,Java編譯器并不會強迫你捕獲它签舞,在運行時很容易看到這個異常發(fā)生榔昔。與此同時,BufferedReader.close可能拋出需要處理的受檢異常IOExceptio瘪菌。如果流關(guān)閉失敗撒会,大多數(shù)程序都不會采取什么有意義的行動,所以捕獲來自close()的異常所需要的代碼就是冗余的樣板代碼师妙。
2.5.2 “try”作為表達式
為了了解JavaK和otlin之間另外一個顯著的差異诵肛,我們修改一下這個例子。讓我們?nèi)サ鬴inally部分(因為你已經(jīng)看過它是怎樣工作的)默穴,并添加一些代碼怔檩,用來打印從文件中讀取的數(shù)字。
fun readerNumber(reader:BufferedReader){
val number = try{
Integer.parseInt(reader.readLine)//變成try表達式的值
}catch(e : NumberFormatException){
return
}
println(number)
}
Kotlin中的try關(guān)鍵字就像if和when一樣蓄诽,引入了一個表達式薛训,可以把它的值賦給一個變量。不同于if,你總是需要用花括號把語句主體括起來仑氛。和其他語句一樣乙埃,如果其主體包含多個表達式,那么整個try表達式的值就是最后一個表達式的值锯岖。
??這個例子將return語句放在catch代碼塊中介袜,因此此函數(shù)的執(zhí)行在catch代碼塊之后不會繼續(xù)。如果你想繼續(xù)執(zhí)行出吹,catch子句也需要一個值遇伞,它將是子句中最后一個表達式的值。下面展示了這是怎么回事捶牢。
fun readerNumber(reader:BufferedReader){
val number = try{
Integer.parseInt(reader.readLine)//沒有任何異常發(fā)生時使用這個值
}catch(e : NumberFormatException){
null //發(fā)生異常情況下使用null
}
println(number)
}
如果一個try代碼塊執(zhí)行一切正常鸠珠,代碼塊中最后一個表達式就是結(jié)果。如果捕獲到了一個異常秋麸,相應(yīng)catch代碼塊中最后一個表達式就是結(jié)果渐排。在上述代碼中,如果捕獲了NumberForMatException,結(jié)果值就是null竹勉。
2.6 小結(jié)
- (1)fun關(guān)鍵字用來聲明函數(shù)飞盆。val關(guān)鍵字和var關(guān)鍵字分別用來聲明只讀變量和可變變量娄琉。
- (2)字符串模板幫助你避免煩瑣的字符串連接次乓。在變量名稱前面加上$前綴或者用${}包圍一個表達式吓歇,來把值注入到字符串中。
- (3)值對象類在Kotlin中以簡潔的方式表示票腰。
- (4)熟悉的if現(xiàn)在是帶返回值的表達式城看。
- (5)when表達式類似于Java中的switch但功能更強大。
- (6)在檢查過變量具有某種類型之后不必顯示地轉(zhuǎn)換它的類型:編譯器使用智能轉(zhuǎn)換自動幫你完成
- (7)for,while和do-while循環(huán)于Java相似杏慰,但是for循環(huán)現(xiàn)在更加方便测柠,特別是當你需要迭代Map的時候,又或者迭代集合需要下標的時候缘滥。
- (8)簡明的語法1..5會創(chuàng)建一個區(qū)間轰胁。區(qū)間和數(shù)列允許Kotlin在for循環(huán)中使用統(tǒng)一的語法和同一套抽象機制,并且還可以使用in運算符和!in運算符來檢查值是否屬于某個區(qū)間朝扼。
- (9)Kotlin中的異常處理和Java非常相似赃阀,除了Kotlin不要求你聲明函數(shù)可以拋出的異常。
第二章的內(nèi)容到這里就結(jié)束了擎颖,謝謝大家的支持榛斯,我也學(xué)習,你們也學(xué)習了搂捧,如果大家喜歡這本《Kotlin實戰(zhàn)》中文版的內(nèi)容驮俗,那就購買一本吧,筆者覺得是一本非常不錯的Kotlin語言學(xué)習書籍允跑,你可以看看筆者總結(jié)的第二章王凑,喜歡就購買一本吧!才70元而已聋丝,知識無價荤崇。