前言
在Kotlin中梦碗,一切都是對象禽绪。沒有像Java中那樣的原始基本類型。這個是非常有幫
助的洪规,因為我們可以使用一致的方式來處理所有的可用的類型印屁。
基本類型
當(dāng)然,像integer斩例,float或者boolean等類型仍然存在雄人,但是它們?nèi)慷紩鳛閷ο?br>
存在的∮K基本類型的名字和它們工作方式都是與Java非常相似的柠衍,但是有一些不同
之處你可能需要考慮到:
- 數(shù)字類型中不會自動轉(zhuǎn)型。舉個例子晶乔,你不能給 Double 變量分配一
個 Int 珍坊。必須要做一個明確的類型轉(zhuǎn)換,可以使用眾多的函數(shù)之一:
val i:Int=7
val d: Double = i.toDouble()
- 字符(Char)不能直接作為一個數(shù)字來處理正罢。在需要時我們需要把他們轉(zhuǎn)換為
一個數(shù)字:
val c:Char='c'
val i: Int = c.toInt()
- 位運算也有一點不同阵漏。在Android中,我們經(jīng)常在 flags 中使用“或”翻具,所以我
使用"and"和"or來舉例:
// Java
int bitwiseOr = FLAG1 | FLAG2;
int bitwiseAnd = FLAG1 & FLAG2;
// Kotlin
val bitwiseOr = FLAG1 or FLAG2
val bitwiseAnd = FLAG1 and FLAG2
還有很多其他的位操作符履怯,比如 sh1 , shs , ushr , xor 或 inv 。當(dāng)我
們需要的時候裆泳,可以在Kotlin官網(wǎng)查看叹洲。
- 字面上可以寫明具體的類型。這個不是必須的工禾,但是一個通用的Kotlin實踐時
省略變量的類型(我們馬上就能看到)运提,所以我們可以讓編譯器自己去推斷出具體的類型。
val i = 12 // An Int
val iHex = 0x0f // 一個十六進制的Int類型
val l = 3L // A Long
val d = 3.5 // A Double
val f = 3.5F // A Float
- 一個String可以像數(shù)組那樣訪問闻葵,并且被迭代:
val s = "Example"
val c = s[2] // 這是一個字符'a'
// 迭代String
val s = "Example"
for(c in s){
print(c)
}
變量
變量可以很簡單地定義成可變( var )和不可變( val )的變量民泵。這個與Java中使
用的 final 很相似。但是不可變在Kotlin(和其它很多現(xiàn)代語言)中是一個很重要
的概念槽畔。
一個不可變對象意味著它在實例化之后就不能再去改變它的狀態(tài)了栈妆。如果你需要一
個這個對象修改之后的版本,那就會再創(chuàng)建一個新的對象。這個讓編程更加具有健
壯性和預(yù)估性鳞尔。在Java中嬉橙,大部分的對象是可變的,那就意味著任何可以訪問它這
個對象的代碼都可以去修改它铅檩,從而影響整個程序的其它地方憎夷。
不可變對象也可以說是線程安全的,因為它們無法去改變昧旨,也不需要去定義訪問控
制,因為所有線程訪問到的對象都是同一個祥得。
所以在Kotlin中兔沃,如果我們想使用不可變性,我們編碼時思考的方式需要有一些改
變级及。一個重要的概念是:盡可能地使用 val 乒疏。除了個別情況(特別是在Android
中,有很多類我們是不會去直接調(diào)用構(gòu)造函數(shù)的)饮焦,大多數(shù)時候是可以的怕吴。
之前提到的另一件事是我們通常不需要去指明類的類型,它會自動從后面分配的語
句中推斷出來县踢,這樣可以讓代碼更加清晰和快速修改转绷。我們在前面已經(jīng)有了一些例
子:
val s = "Example" // A String
val i = 23 // An Int
val actionBar = supportActionBar // An ActionBar in an Activity
如果我們需要使用更多的范型類型,則需要指定:
val a: Any = 23
val c: Context = activity
屬性
屬性與Java中的字段是相同的硼啤,但是更加強大议经。屬性做的事情是字段加上getter加
上setter。我們通過一個例子來比較他們的不同之處谴返。這是Java中字段安全訪問和
修改所需要的代碼:
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
···
Person person = new Person();
person.setName("name");
String name = person.getName();
在Kotlin中煞肾,只需要一個屬性就可以了:
public class Person {
var name: String = ""
}
···
val person = Person()
person.name = "name"
val name = person.name
如果沒有任何指定,屬性會默認(rèn)使用getter和setter嗓袱。當(dāng)然它也可以修改為你自定義
的代碼籍救,并且不修改存在的代碼:
public classs Person {
var name: String = ""
get() = field.toUpperCase()
set(value){
field = "Name: $value"
}
}
如果需要在getter和setter中訪問這個屬性自身的值,它需要創(chuàng)建一個 backing
field 渠抹◎迹可以使用 field 這個預(yù)留字段來訪問,它會被編譯器找到正在使用的并
自動創(chuàng)建逼肯。需要注意的是耸黑,如果我們直接調(diào)用了屬性,那我們會使用setter和getter
而不是直接訪問這個屬性篮幢。 backing field 只能在屬性訪問器內(nèi)訪問大刊。
就如在前面章節(jié)提到的,當(dāng)操作Java代碼的時候,Kotlin將允許使用屬性的語法去
訪問在Java文件中定義的getter/setter方法缺菌。編譯器會直接鏈接到它原始的
getter/setter方法葫辐。所以當(dāng)我們直接訪問屬性的時候不會有性能開銷。
Anko是什么伴郁?
Anko是JetBrains開發(fā)的一個強大的庫耿战。它主要的目的是用來替代以前XML的方式
來使用代碼生成UI布局。這是一個很有趣的特性焊傅,我推薦你可以嘗試下剂陡,但是我在
這個項目中暫時不使用它。對于我(可能是由于多年的UI繪制經(jīng)驗)來說使用XML
更容易一些狐胎,但是你會喜歡那種方式的鸭栖。
然而,這個不是我們能在這個庫中得到的唯一一個功能握巢。Anko包含了很多的非常有
幫助的函數(shù)和屬性來避免讓你寫很多的模版代碼晕鹊。我們將會通過本書見到很多例
子,但是你應(yīng)該快速地認(rèn)識到這個庫幫你解決了什么樣的問題暴浦。
盡管Anko是非常有幫助的溅话,但是我建議你要理解這個背后到底做了什么。你可以在
任何時候使用 ctrl + 點擊 (Windows)或者 cmd + 點擊 (Mac)的方式跳轉(zhuǎn)
到Anko的源代碼歌焦。Anko的實現(xiàn)方式對學(xué)習(xí)大部分的Kotlin語言都是非常有幫助的飞几。
開始使用Anko
在之前,讓我們來使用Anko來簡化一些代碼同规。就像你將看到的循狰,任何時候你使用了
Anko庫中的某些東西,它們都會以屬性名券勺、方法等方式被導(dǎo)入绪钥。這是因為Anko使
用了擴展函數(shù)在Android框架中增加了一些新的功能。我們將會在以后看到擴展函
數(shù)是什么关炼,怎么去編寫它程腹。
在 MainActivity:onCreate ,一個Anko擴展函數(shù)可以被用來簡化獲取一個
RecyclerView:
val forecastList: RecyclerView = find(R.id.forecast_list)
我們現(xiàn)在還不能使用庫中更多的東西儒拂,但是Anko能幫助我們簡化代碼寸潦,比如,實例
化Intent社痛,Activity之間的跳轉(zhuǎn)见转,F(xiàn)ragment的創(chuàng)建,數(shù)據(jù)庫的訪問蒜哀,Alert的創(chuàng)建……
我們將會在實現(xiàn)這個App的過程中學(xué)習(xí)到很多有趣的例子斩箫。
擴展函數(shù)
擴展函數(shù)數(shù)是指在一個類上增加一種新的行為,甚至我們沒有這個類代碼的訪問權(quán)
限。這是一個在缺少有用函數(shù)的類上擴展的方法乘客。在Java中狐血,通常會實現(xiàn)很多帶有
static方法的工具類。Kotlin中擴展函數(shù)的一個優(yōu)勢是我們不需要在調(diào)用方法的時候
把整個對象當(dāng)作參數(shù)傳入易核。擴展函數(shù)表現(xiàn)得就像是屬于這個類的一樣匈织,而且我們可
以使用 this 關(guān)鍵字和調(diào)用所有public方法。
舉個例子牡直,我們可以創(chuàng)建一個toast函數(shù)缀匕,這個函數(shù)不需要傳入任何context,它可以
被任何Context或者它的子類調(diào)用碰逸,比如Activity或者Service:
fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, duration).show()
}
這個方法可以在Activity內(nèi)部直接調(diào)用:
toast("Hello world!")
toast("Hello world!", Toast.LENGTH_LONG)
當(dāng)然弦追,Anko已經(jīng)包括了自己的toast擴展函數(shù),跟上面這個很相似花竞。Anko提供了一
些針對 CharSequence 和 resource 的函數(shù),還有兩個不同的toast和longToast方
法:
toast("Hello world!")
longToast(R.id.hello_world)
擴展函數(shù)也可以是一個屬性掸哑。所以我們可以通過相似的方法來擴展屬性约急。下面的例
子展示了使用他自己的getter/setter生成一個屬性的方式。Kotlin由于互操作性的特
性已經(jīng)提供了這個屬性苗分,但理解擴展屬性背后的思想是一個很不錯的練習(xí):
public var TextView.text: CharSequence
get() = getText()
set(v) = setText(v)
擴展函數(shù)并不是真正地修改了原來的類厌蔽,它是以靜態(tài)導(dǎo)入的方式來實現(xiàn)的。擴展函
數(shù)可以被聲明在任何文件中摔癣,因此有個通用的實踐是把一系列有關(guān)的函數(shù)放在一個
新建的文件里奴饮。
這是Anko功能背后的魔法。現(xiàn)在通過以上择浊,你也可以自己創(chuàng)建你的魔法戴卜。
執(zhí)行一個請求
對于感受我們要實現(xiàn)的想法而言,我們目前的文本是很好開始琢岩,但是現(xiàn)在是時候去
請求一些顯示在RecyclerView上的真正的數(shù)據(jù)了投剥。我們將會使用OpenWeatherMap
API來獲取數(shù)據(jù),還有一些普通類來現(xiàn)實這個請求担孔。多虧Kotlin非常強大的互操作
性江锨,你可以使用任何你想使用的庫,比如用Retrofit來執(zhí)行服務(wù)器請求糕篇。當(dāng)只是執(zhí)行
一個簡單的API請求啄育,我們可以不使用任何第三方庫來簡單地實現(xiàn)。
而且拌消,如你所見挑豌,Kotlin提供了一些擴展函數(shù)來讓請求變得更簡單。首先,我們要
創(chuàng)建一個新的Request類:
public class Request(val url: String) {
public fun run() {
val forecastJsonStr = URL(url).readText()
Log.d(javaClass.simpleName, forecastJsonStr)
}
}
我們的請求很簡單地接收一個url浮毯,然后讀取結(jié)果并在logcat上打印json完疫。實現(xiàn)非常
簡單,因為我們使用 readText 债蓝,這是Kotlin標(biāo)準(zhǔn)庫中的擴展函數(shù)壳鹤。這個方法不推
薦結(jié)果很大的響應(yīng),但是在我們這個例子中已經(jīng)足夠好了饰迹。
如果你用這些代碼去比較Java芳誓,你會發(fā)現(xiàn)我們僅使用標(biāo)準(zhǔn)庫就節(jié)省了大量的代碼。
比如 HttpURLConnection 啊鸭、 BufferedReader 和需要達到相同效果所必要的迭
代結(jié)果锹淌,管理連接狀態(tài)、reader等部分的代碼赠制。很明顯赂摆,這些就是場景背后函數(shù)所
作的事情,但是我們卻不用關(guān)心钟些。
為了可以執(zhí)行請求烟号,App必須要有Internet權(quán)限。所以需要
在 AndroidManifest.xml 中添加:
<uses-permission android:name="android.permission.INTERNET" />
在主線程以外執(zhí)行請求
如你所知政恍,HTTP請求不被允許在主線程中執(zhí)行汪拥,否則它會拋出異常。這是因為阻
塞住UI線程是一個非常差的體驗篙耗。Android中通用的做法是使用 AsyncTask 迫筑,但是
這些類是非常丑陋的,并且使用它們無任何副作用地實現(xiàn)功能也是非常困難的宗弯。如
果你使用不小心脯燃, AsyncTasks 會非常危險,因為當(dāng)運行到 postExecute 時罕伯,如
果Activity已經(jīng)被銷毀了曲伊,這里就會崩潰。
Anko提供了非常簡單的DSL來處理異步任務(wù)追他,它滿足大部分的需求坟募。它提供了一個
基本的 async 函數(shù)用于在其它線程執(zhí)行代碼,也可以選擇通過調(diào)用 uiThread 的
方式回到主線程邑狸。在子線程中執(zhí)行請求如下這么簡單:
async() {
Request(url).run()
uiThread { longToast("Request performed") }
}
UIThread 有一個很不錯的一點就是可以依賴于調(diào)用者懈糯。如果它是被一
個 Activity 調(diào)用的,那么如果 activity.isFinishing() 返回 true 单雾,
則 uiThread 不會執(zhí)行赚哗,這樣就不會在Activity銷毀的時候遇到崩潰的情況了她紫。
假如你想使用 Future 來工作, async 返回一個Java Future 屿储。而且如果你需
要一個返回結(jié)果的 Future 贿讹,你可以使用 asyncResult 。
真的很簡單够掠,對吧民褂?而且比 AsyncTasks 更加具有可讀性。現(xiàn)在疯潭,我僅僅給請求
發(fā)送了一個url赊堪,來測試我們是否可以正確接收內(nèi)容,這樣我們才能在Activity中把它
畫出來竖哩。我很快會講到怎么去進行json解析和轉(zhuǎn)換成app中的數(shù)據(jù)類哭廉,但是在我們
繼續(xù)之前,學(xué)習(xí)什么是數(shù)據(jù)類也是很重要的相叁。
檢查代碼并審查url請求和包結(jié)構(gòu)的代碼遵绰。你可以運行app并且確保你可以在打印的
json日志和請求完畢之后的toast。