寫給Android開(kāi)發(fā)者的Kotlin入門

Google在今年的IO大會(huì)上宣布,將Android開(kāi)發(fā)的官方語(yǔ)言更換為Kotlin,作為跟著Google玩兒Android的人,我們必須盡快了解和使用Kotlin語(yǔ)言。

不過(guò)Kotlin畢竟是語(yǔ)言級(jí)別的新事物,比起Java來(lái)說(shuō),從編程思想到代碼細(xì)節(jié)都有不少變化,我們最好先對(duì)Kotlin有個(gè)整體的基本的了解,然后再去學(xué)習(xí)和使用,這樣才能高效地掌握Kotlin語(yǔ)言。

Java的輝煌與陰影

1995年,當(dāng)年如日中天的Sun公司發(fā)布了Java語(yǔ)言,引起了巨大的轟動(dòng),與當(dāng)時(shí)主流的C語(yǔ)言和Basic語(yǔ)言比起來(lái),Java語(yǔ)言簡(jiǎn)單、面向?qū)ο蟆⒎€(wěn)定、與平臺(tái)無(wú)關(guān)、解釋型、多線程、動(dòng)態(tài)等特點(diǎn),就像是打開(kāi)了一個(gè)新的世界,一時(shí)間風(fēng)靡全球,云集者眾,微軟為了模仿Java搞出C#語(yǔ)言,Netscape為了趕時(shí)髦硬塞出一個(gè)JavaScript語(yǔ)言,IBM則捏著鼻子做了Java IDE Eclipse(日蝕,呵呵)。直到現(xiàn)在,Java在編程世界里還占據(jù)著舉足輕重的地位,Andy Rubin在開(kāi)發(fā)Android系統(tǒng)時(shí),也很自然地采用了Java和C++(C++負(fù)責(zé)NDK開(kāi)發(fā))作為開(kāi)發(fā)語(yǔ)言。

但是,Java畢竟是20多年前的語(yǔ)言了,雖然有不斷擴(kuò)展更新,但是底層設(shè)計(jì)思想是很難改動(dòng)的,這就導(dǎo)致它很難實(shí)現(xiàn)一些新的語(yǔ)言特性,例如函數(shù)式編程、Lambda 表達(dá)式、流式API、高階函數(shù)、空指針安全等(雖然Java8實(shí)現(xiàn)了部分特性,但是Android還不怎么支持Java8),這些新的語(yǔ)言特性大受好評(píng),可以說(shuō)解放了編程的生產(chǎn)力,這其實(shí)也說(shuō)明了一個(gè)事實(shí):開(kāi)發(fā)效率/時(shí)間是軟件公司真正的瓶頸,任何能壓縮代碼量,提高開(kāi)發(fā)效率的舉措,都應(yīng)該受到重視。

而且,Android還存在Java版權(quán)危機(jī)的問(wèn)題,收購(gòu)了Sun公司的Oracle曾向Google索要巨額的Java版權(quán)費(fèi),這可能也加快了Google尋找Android開(kāi)發(fā)替代語(yǔ)言的動(dòng)作。

蘋果公司已經(jīng)在用Swift語(yǔ)言替代Object-C語(yǔ)言,Google也找到了替代Java的語(yǔ)言,也就是JetBrains公司(Android Studio也是用該公司的Intelli J改的)主推的Kotlin。

其實(shí),Swift和Kotlin還挺相似的,有一篇Swift is like Kotlin對(duì)這兩種語(yǔ)言做過(guò)簡(jiǎn)單的對(duì)比。

Kotlin的出現(xiàn)

Kotlin也是基于JVM設(shè)計(jì)的編程語(yǔ)言,算是對(duì)Java的溫和改良,她是一個(gè)開(kāi)源項(xiàng)目的成果,擁有很高的聲望,很多公司、組織、業(yè)界大犇都很喜歡她,Square公司的Jake大神(Dagger、ButterKnife、Retrofit、OkHttp...之父)就專門寫了篇Using Project Kotlin for Android為Kotlin站臺(tái)。

相對(duì)Java來(lái)說(shuō),Kotlin在編寫代碼時(shí)有如下優(yōu)勢(shì):代碼簡(jiǎn)潔高效、函數(shù)式編程、空指針安全、支持lambda表達(dá)式、流式API等。

執(zhí)行效率上,Kotlin和Java具有同樣的理論速度(都是編譯成JVM字節(jié)碼)。

另外,新語(yǔ)言必須考慮兼容性,為了與存量項(xiàng)目代碼和諧共處,Kotlin和Java是互相完美兼容的,兩種代碼文件可以并存,代碼可以互相調(diào)用文件可以互相轉(zhuǎn)換,庫(kù)文件也可以無(wú)障礙地互相調(diào)用,據(jù)說(shuō)使用Kotlin基本不會(huì)帶來(lái)額外的成本負(fù)擔(dān)。

編程語(yǔ)言本質(zhì)上還是工具,要運(yùn)用工具提高效率和質(zhì)量,還要看具體開(kāi)發(fā)者,我們先看看Kotlin相對(duì)Java有哪些特色。

Kotlin的特色

Kotlin作為Java的改良,在Android開(kāi)發(fā)中有很多優(yōu)勢(shì),我們先從相對(duì)直觀的界面繪制開(kāi)始了解,然后看看Kotlin的語(yǔ)法特點(diǎn),再慢慢去接觸更深層次的編程思想。

簡(jiǎn)化findViewById

我們知道,Android的架構(gòu)里,xml布局文件和Activity是松耦合的,Activity中要使用界面元素,必須借助R文件對(duì)xml控件的記錄,用findViewById找到這個(gè)元素。

Kotlin中我們可繼續(xù)使用findViewById去綁定xml布局中的控件:(TextView)findViewById(R.id.hello);

進(jìn)一步引用Anko之后,可以使用find函數(shù)去綁定控件:find(R.id.hello),不需要類型轉(zhuǎn)換

同時(shí),Kotlin還提供一種更激進(jìn)的方法,通過(guò)在gradule中引用applyplugin:'kotlin-android-extensions',徹底取消findViewById這個(gè)函數(shù),具體做法如下:

首先,在app的gradule中,添加引用

添加引用

然后,在Activity中直接根據(jù)id使用界面元素

直接操作界面元素

按住Ctrl鍵,會(huì)提示我們這個(gè)控件詳情

提示詳情

點(diǎn)擊后,可以直接跳轉(zhuǎn)到xml文件中的控件位置,光標(biāo)會(huì)停留在Id處

直接跳轉(zhuǎn)到控件Id處

這種特性令人聯(lián)想起C#語(yǔ)言中對(duì)界面控件的管理,在C#里,界面的控件可以直接調(diào)用,不需要find,這是因?yàn)樵趧?chuàng)建一個(gè)Form1.cs界面文件時(shí),IDE會(huì)自動(dòng)創(chuàng)建一個(gè)對(duì)應(yīng)的額Form1.designer.cs類,在這個(gè)類里,自動(dòng)管理所有界面控件的對(duì)象。

Kotlin也是類似的思路,它會(huì)遍歷你的xml文件,創(chuàng)建對(duì)應(yīng)的虛擬包給你引用(用Alt+Enter引用),我們使用的控件對(duì)象,其實(shí)是這個(gè)虛擬包里的控件對(duì)象。

引用虛擬界面類

為什么說(shuō)這個(gè)包是虛擬的,因?yàn)樗莐otlin臨時(shí)創(chuàng)建的,你無(wú)法打開(kāi)它的文件,在編譯apk時(shí),Kotlin會(huì)自動(dòng)幫你補(bǔ)充findViewbyId的代碼,最終得到的產(chǎn)品其實(shí)沒(méi)變,它只是方便了程序員的書寫。

Anko

Anko其實(shí)是一種DSL(領(lǐng)域相關(guān)語(yǔ)言),是專門用代碼方式來(lái)寫界面和布局的。

上一節(jié)針對(duì)findViewById,最激進(jìn)的方式是取消這個(gè)函數(shù),這一節(jié)更加激進(jìn),我們可以連XML布局文件也取消掉。

在XML中定義界面布局當(dāng)然是有好處的,分層清晰,代碼易讀,現(xiàn)在AS中預(yù)覽效果也不錯(cuò)。但是它渲染過(guò)程復(fù)雜,難以重用(雖然有including),而如果我們用Java代碼去替換xml,代碼會(huì)更加復(fù)雜和晦澀。

Anko卻實(shí)現(xiàn)了在代碼中簡(jiǎn)潔優(yōu)雅地定義界面和布局,而且由于不需要讀取和解析XML布局文件,Anko的性能表現(xiàn)更佳。

我們可以看看Anko在Github上的代碼示例,用6行代碼就做出了一個(gè)有輸入框、按鈕、點(diǎn)擊事件和Toast的界面和功能

示例的界面和功能

我們自己寫一下這6行代碼,首先需要在gradle中添加引用,主要是sdk和v4/v7包

Gradle中引用Anko

然后參照Anko在Github中的示例,實(shí)現(xiàn)這6行代碼。

參考Github的Anko實(shí)例代碼

Activity本來(lái)會(huì)在加載時(shí)在onCreate函數(shù)里用setContentView函數(shù)來(lái)尋找布局文件,并加載為自己的界面,在這里,Anko代碼替代了setContentView,直接告訴Activity應(yīng)該如何繪制界面。

(在Fragment里不可以這樣直接寫verticalLayout,因?yàn)榧虞d機(jī)制不一樣,F(xiàn)ragment需要在onCreateView函數(shù)里inflate并返回一個(gè)View對(duì)象,所以對(duì)應(yīng)的Anko代碼也需要寫在onCreateView函數(shù)里并返回一個(gè)View,可以用return with(context){verticalLayout[...]}或者return UI{verticalLayout[...]}.view)

可以看到,代碼非常簡(jiǎn)潔干練,不像以往的Android代碼那樣拖沓,這既與Kotlin的語(yǔ)法有關(guān),也與Anko能用代碼實(shí)現(xiàn)界面和布局有關(guān)。

這段代碼雖然簡(jiǎn)潔,可是卻失去了MVC分層的好處,因?yàn)閂直接寫在業(yè)務(wù)代碼里了,這個(gè)問(wèn)題好解決,我們可以把Anko布局代碼放到一個(gè)專門的類文件里

抽出Anko布局代碼

然后在Activity引用這個(gè)布局類來(lái)繪制界面

引用Anko布局類

雖然Anko效率很高,代碼簡(jiǎn)潔,清爽直觀,但是目前還有很多坑,主要包括:

1.AS并不支持直接預(yù)覽Anko界面,雖然有個(gè)Anko DSL Preview插件,但是需要make才能刷新,而且和現(xiàn)在的AS不兼容。

2.如果要在多版本中動(dòng)態(tài)替換外部資源,需要用動(dòng)態(tài)類加載才能實(shí)現(xiàn),無(wú)法借用資源apk實(shí)現(xiàn)。

3.不方便根據(jù)view的id去即時(shí)引用view控件(R文件和inflate這時(shí)反而更加靈活)。

另外,Anko還在異步、日志、Toast、對(duì)話框、數(shù)據(jù)庫(kù)等方面提供優(yōu)化服務(wù),是否采用就看自身需要了。

Kotlin語(yǔ)法特點(diǎn)

看了上面這些例子,我們發(fā)現(xiàn)Kotlin本身的語(yǔ)法和Java有些不一樣,新語(yǔ)言嘛,相對(duì)Java而言,主要的變化有這么幾條:

1.沒(méi)有“;”

在Kotlin語(yǔ)法里,代碼行不需要用“;”結(jié)尾,什么都不寫就好

2.重要的“:”

在Java里,“:”主要在運(yùn)算符里出現(xiàn)(for/switch/三元運(yùn)算符等)。

在Kotlin里,“:”的地位大大提升了,它的用途非常廣泛,包括:

定義變量類型

var name:String="my name" //變量name為String類型

定義參數(shù)的類型

fun makeTool(id:Int){ //參數(shù)id為Int類型

}

定義函數(shù)的返回值

fun getAddr(id:Int):String{ //返回值為String類型

}

聲明類/接口的繼承

class KotlinActivityUI :AnkoComponent<KotlinActivity>{//繼承AnkoComponent接口

使用Java類

val intent = Intent(this, MainActivity::class.java) //需要用::來(lái)使用Java類,注意是兩個(gè)“”

3.沒(méi)有“new”

Kotlin實(shí)例化一個(gè)對(duì)象時(shí)不需要new關(guān)鍵字

var list=ArrayList()

4.變量、常量、類型推斷

用var定義變量(像js)

var name:String="my name"

用val定義常量(相當(dāng)于final)

val TAG:String="ClassName"

上面兩個(gè)例子用:String來(lái)定義了數(shù)據(jù)類型,這個(gè)是可以省略的,Kotlin支持類型推斷,這兩句話你可以寫成

var name="my name"

val TAG="ClassName"

5.初始化和延遲加載

在Java里,我們可以定義一個(gè)變量,但是并不賦值(int和boolean會(huì)有默認(rèn)值)

但是Kotlin里必須為變量賦值,如果只寫一個(gè)變量,卻不賦值,像下面這樣:

var name

編譯器會(huì)報(bào)錯(cuò),提示你未初始化,你必須賦值為0或者null,或者別的什么值。

不過(guò),我們有時(shí)候就是不能在定義變量時(shí)就初始化它,比如在Android中我們經(jīng)常預(yù)定義一個(gè)View控件而不初始化,但是直到onCreate或onCreateView時(shí)才初始化它。

針對(duì)這種情況,Kotlin提供了懶加載lazy機(jī)制來(lái)解決這個(gè)問(wèn)題,在懶加載機(jī)制里,變量只有在第一次被調(diào)用時(shí),才會(huì)初始化,代碼需要這樣寫

在初次調(diào)用時(shí)初始化變量的lazy機(jī)制

lazy只適用于val對(duì)象,對(duì)于var對(duì)象,需要使用lateinit,原理是類似的,只是代碼需要這樣寫

var變量使用lateinit機(jī)制

6.空指針安全

在Kotlin里,可以用“?”表示可以為空,也可以用“!!”表示不可以為空。

空指針安全并不是不需要處理空指針,你需要用“?”聲明某個(gè)變量是允許空指針的,例如:

var num:Int?=null

聲明允許為空時(shí),不能使用類型推斷,必須聲明其數(shù)據(jù)類型

空指針雖然安全了,但對(duì)空指針的處理還是要視情況而定,有時(shí)候不處理,有時(shí)候做數(shù)據(jù)檢查,有時(shí)候還需要拋出異常,這三種情況可以這樣寫:

val v1 =num?.toInt() //不做處理返回 null

val v2 =num?.toInt() ?:0 //判斷為空時(shí)返回0

val v3 =num!!.toInt() //拋出空指針異常(用“!!”表示不能為空)

更多空指針異常處理,有一篇NullPointException 利器 Kotlin 可選型介紹的比較全面,值得借鑒

7.定義函數(shù)

在Kotlin語(yǔ)法里,定義函數(shù)的格式是這樣的

fun 方法名(參數(shù)名:類型,參數(shù)名:類型...) :返回類型{

}

所以,一般來(lái)說(shuō),函數(shù)是這樣寫的

fun getAddress(id:Int,name:String):String{

? ? return"got it"

}

由于Kotlin可以對(duì)函數(shù)的返回值進(jìn)行類型推斷,所以經(jīng)常用“=”代替返回類型和“return”關(guān)鍵字,上面這段代碼也可以寫成

fun getAddress(id:Int,name:String)={ //用“=”代替return,返回String類型則交給類型推斷

? ? ?"got it" //return被“=”代替了

}

如果函數(shù)內(nèi)代碼只有一行,我們甚至可以去掉{}

fun getAddress(id:Int,name:String)="got it" //去掉了{(lán)}

}

函數(shù)也允許空指針安全,在返回類型后面增加“?”即可

fun getAddress(id:Int,name:String) :String?="got it"

有時(shí)候,函數(shù)的返回類型是個(gè)Unit,這其實(shí)就是Java中的void,表示沒(méi)有返回

fun addAddress(id:Int,name:String):Unit{ //相當(dāng)于java的void

}

不過(guò),在函數(shù)無(wú)返回時(shí),一般不寫Unit

fun addAddress(id:Int,name:String){ //相當(dāng)于java的void

}

8.用is取代了instance of

代碼很簡(jiǎn)單

if(obj is String)...

9.in、區(qū)間和集合

Kotlin里有區(qū)間的概念,例如1..5表示的就是1-5的整數(shù)區(qū)間

可以用in判斷數(shù)字是否在某個(gè)區(qū)間

if(x in 1..5){ ...//檢查x數(shù)值是否在1到5區(qū)間

可以用in判斷集合中是否存在某個(gè)元素

if(name in list){...//檢查list中是否有某個(gè)元素(比Java簡(jiǎn)潔多了)

可以用in遍歷整個(gè)集合

for(i in 1..5){ ...//遍歷1到5

for(item in list){...//遍歷list中的每個(gè)元素(相當(dāng)于Java的for(String item : list))

另外,in在遍歷集合時(shí)功能相當(dāng)強(qiáng)大:

在遍歷集合時(shí),可以從第N項(xiàng)開(kāi)始遍歷

for(i in 3..list.size-2){...相當(dāng)于for (int i = 3; i <= list.size()-2; i++)

可以倒序遍歷

for(i in list.size downTo 0) {...相當(dāng)于for (int i = list.size(); i >= 0; i--)

可以反轉(zhuǎn)列表

for(i in (1..5).reversed())

可以指定步長(zhǎng)

for(i in 1.0..2.0 step 0.3) //步長(zhǎng)0.3

Kotlin里的集合還都自帶foreach函數(shù)

list.forEach {...

10.用when取代了switch

switch在Java里一直不怎么給力,在稍老一些的版本里,甚至不支持String

Kotlin干脆用強(qiáng)大的when取代了switch,具體用法如下

when的用法

代碼中的參數(shù)類型Any,相當(dāng)于Java中的Obejct,是Kotlin中所有類的基類,至于object關(guān)鍵字,在Kotlin中另有用處...

11.字符串模板

在Java里使用字符串模板沒(méi)有難度,但是可讀性較差,代碼一般是

MessageFormat.format("{0}xivehribuher{1}xhvihuehewogweg",para0,para2);

在字符串較長(zhǎng)時(shí),你就很難讀出字符串想表達(dá)什么

在kotlin里,字符串模板可讀性更好

"${para0}xivehribuher${para1}xhvihuehewogweg"

12.數(shù)據(jù)類

數(shù)據(jù)類是Kotlin相對(duì)Java的一項(xiàng)重大改進(jìn),我們?cè)贘ava里定義一個(gè)數(shù)據(jù)Model時(shí),要做的事情有很多,例如需要定義getter/setter(雖然有插件代寫),需要自己寫equals(),hashCode(),copy()等函數(shù)(部分需要手寫)

但是在Kotlin里,你只需要用data修飾class的一行代碼

data class Client(var id:Long,var name:String,var birth:Int,var addr:String)

Kotlin會(huì)自動(dòng)幫你實(shí)現(xiàn)前面說(shuō)的那些特性。

數(shù)據(jù)模型里經(jīng)常需要一些靜態(tài)屬性或方法,Kotlin可以在數(shù)據(jù)類里添加一個(gè)companion object(伴隨對(duì)象),讓這個(gè)類的所有對(duì)象共享這個(gè)伴隨對(duì)象(object在Kotlin中用來(lái)表示單例,Kotlin用Any來(lái)表示所有類的基類)

添加companion obejct

13.單例模式

單例是很常見(jiàn)的一種設(shè)計(jì)模式,Kotlin干脆從語(yǔ)言級(jí)別提供單例,關(guān)鍵字為object,如果你在擴(kuò)展了Kotlin的IDE里輸入singleton,IDE也會(huì)自動(dòng)幫你生成一個(gè)伴隨對(duì)象,也就是一個(gè)單例

單例類

如果一個(gè)類需要被定義為class,又想做成單例,就需要用上一節(jié)中提到的companion object

例如,如果我們用IDE新建一個(gè)blankFragment,IDE會(huì)自動(dòng)幫我們寫出下面的代碼,這本來(lái)是為了解決Fragment初始化時(shí)傳值的問(wèn)題,我們注意到她已經(jīng)使用了companion object單例

自動(dòng)生成的代碼

如果我們修改一下newInstance這個(gè)函數(shù)

改成單例

那么,我們用

BlankFragment.newInstance()

就可以調(diào)用這個(gè)fragment的單例了

14.為已存在的類擴(kuò)展方法和屬性

為了滿足開(kāi)放封閉原則,類是允許擴(kuò)展,同時(shí)嚴(yán)禁修改的,但是實(shí)現(xiàn)擴(kuò)展并不輕松,在Java里,我們需要先再造一個(gè)新的類,在新類里繼承或者引用舊類,然后才能在新類里擴(kuò)展方法和屬性,實(shí)際上Java里層層嵌套的類也非常多。

在Kotlin里,這就簡(jiǎn)潔優(yōu)雅地多,她允許直接在一個(gè)舊的類上做擴(kuò)展,即使這是一個(gè)final類。

例如,Android中常見(jiàn)的Toast,參數(shù)較多,寫起來(lái)也相對(duì)繁瑣,我們一般是新建一個(gè)Util類去做一個(gè)相對(duì)簡(jiǎn)單的函數(shù),比如叫做showLongToast什么的,我們不會(huì)想在Activity或Fragment中擴(kuò)展這個(gè)函數(shù),因?yàn)樘闊覀冃枰^承Activity做一個(gè)比如叫ToastActivity的類,在里面擴(kuò)展showLongToast函數(shù),然后把業(yè)務(wù)Activity改為繼承這個(gè)ToastActivity...

在Kotlin里,我們只需要這樣寫

擴(kuò)展函數(shù)

就完成了Activity類的函數(shù)擴(kuò)展,我們可以在Activity及其子類里隨意調(diào)用了

調(diào)用擴(kuò)展函數(shù)

需要注意的是,你無(wú)法用擴(kuò)展去覆蓋已存在的方法,例如,Activity里已經(jīng)有一個(gè)onBackPressed方法,那么你再擴(kuò)展一個(gè)Activity.onBackPressed方法是無(wú)用的,當(dāng)你調(diào)用Activity().onBackPressed()時(shí),它只會(huì)指向Activity本身的那個(gè)onBackPressed方法。

我們還可以用類似的方式去擴(kuò)展屬性

擴(kuò)展屬性

不過(guò),Kotlin的擴(kuò)展其實(shí)是偽裝的,我們并沒(méi)有真正給Activity類擴(kuò)展出新的函數(shù)或?qū)傩?,你在A類里為Activity擴(kuò)展了函數(shù),換到B類里,你就找不到這個(gè)函數(shù)了。

這是因?yàn)?,Kotlin為類擴(kuò)展函數(shù)時(shí),并沒(méi)有真的去修改對(duì)應(yīng)的類文件,只是借助IDE和編譯器,使他看起來(lái)像擴(kuò)展而已。

所以,如果類的某些函數(shù)只在特殊場(chǎng)景下使用,可以使用靈活簡(jiǎn)潔的擴(kuò)展函數(shù)來(lái)實(shí)現(xiàn)。

但是,如果想為類永久性地添加某些新的特性,還是要利用繼承或者裝飾模式(decorator)。

不過(guò),Kotlin里對(duì)于類的家族定義和Java有所不同,我們來(lái)看一下

15.類的家族結(jié)構(gòu)

Kotlin關(guān)于類的家族結(jié)構(gòu)的設(shè)計(jì),和Java基本相似,但是略有不同:

Object:取消,在Java里Object是所有類的基類,但在Kotlin里,基類改成了Any

Any:新增,Kotlin里所有類的基類

object:新增,Kotlin是區(qū)分大小寫的,object是Kotlin中的單例類

new:取消,Kotlin不需要new關(guān)鍵字

private: 仍然表示私有

protected: 類似private,在子類中也可見(jiàn)

internal: 模塊內(nèi)可見(jiàn)

inner:內(nèi)部類

public: 仍然表示共有,但是Kotlin的內(nèi)部類和參數(shù)默認(rèn)為public

abstract:仍然表示抽象類

interface:仍然表示接口

final:取消,Kotlin的繼承和Java不同,Java的類默認(rèn)可繼承,只有final修飾的類不能繼承;Kotlin的類默認(rèn)不能繼承,只有為open修飾的類能繼承

open:新增,作用見(jiàn)上一條

static:取消!Java用static去共享同一塊內(nèi)存空間,這是一個(gè)非常實(shí)用的設(shè)計(jì),不過(guò)Kotlin移除了static,用伴隨對(duì)象(前面提到過(guò)的compaion object)的概念替換了static,伴隨對(duì)象其實(shí)是個(gè)單例的實(shí)體,所以伴隨對(duì)象比static更加靈活一些,能去繼承和擴(kuò)展。

繼承:在Kotlin里,繼承關(guān)系統(tǒng)一用“:”,不需要向java那樣區(qū)分implement和extend,在繼承多個(gè)類/接口時(shí),中間用“,”區(qū)分即可,另外,在繼承類時(shí),類后面要跟()。所以在Kotlin里,繼承類和接口的代碼一般是這樣的:

class BaseClass : Activity(), IBinder{ //示例

16.構(gòu)造函數(shù)

在Java里,類的構(gòu)造函數(shù)是這樣的

public 類名作為函數(shù)名 (參數(shù)) {...}

Java里有時(shí)會(huì)重載多個(gè)構(gòu)造函數(shù),這些構(gòu)造函數(shù)都是并列的

在Kotlin里,類也可以有多個(gè)構(gòu)造函數(shù)(constructor),但是分成了1個(gè)主構(gòu)造函數(shù)和N個(gè)二級(jí)構(gòu)造函數(shù),二級(jí)構(gòu)造函數(shù)必須直接或間接代理主構(gòu)造函數(shù),也就是說(shuō),在Kotlin里,主構(gòu)造函數(shù)有核心地位

主構(gòu)造函數(shù)一般直接寫在類名后面,像這么寫

class ClientInfo(id:Long,name:String,addr:String){

這其實(shí)是個(gè)縮寫,完全版本應(yīng)該是

class ClientInfo constructor(id:Long,name:String,addr:String){

主構(gòu)造函數(shù)的這個(gè)結(jié)構(gòu),基本決定了,在這個(gè)主構(gòu)造函數(shù)里,沒(méi)法寫初始化代碼...

而二級(jí)構(gòu)造函數(shù)必須代理主構(gòu)造函數(shù),寫出來(lái)的效果是這樣的

二級(jí)構(gòu)造函數(shù)

17.初始化模塊init

上一節(jié)提到過(guò),主構(gòu)造函數(shù)里不能寫代碼,這就很麻煩了,不過(guò)還好,Kotlin提供了初始化模塊,基本上就是用init修飾符修飾一個(gè){},在類初始化時(shí)執(zhí)行這段兒代碼,代碼像這樣寫就行

初始化模塊

18.其他

Kotlin還有很多其他的語(yǔ)言特性,本文主要是為了建立對(duì)Kotlin的大概印象,更多細(xì)節(jié)就不再列舉了,建議仔細(xì)閱讀Kotlin官方文檔,并且多動(dòng)手寫一些代碼。

函數(shù)式編程

讀到這里,我們發(fā)現(xiàn)熟悉Java的人好像很容易學(xué)會(huì)Kotlin,甚至?xí)杏X(jué)Kotlin不像一門新語(yǔ)言。但語(yǔ)法只是讓我們能用Kotlin,要想用好Kotlin,就必須理解Kotlin背后的函數(shù)式編程理念。

一個(gè)用慣了錘子的人,看什么都像是釘子,我們必須先扔掉錘子,再去理解函數(shù)式編程。

我們先重新理解一下什么是計(jì)算機(jī),什么是編程:

1.計(jì)算機(jī):人發(fā)明計(jì)算機(jī)是為了計(jì)算數(shù)據(jù)(二戰(zhàn)期間為了把炮彈打得更準(zhǔn),需要解大量的微積分,就造了臺(tái)計(jì)算機(jī)幫忙,我們知道第一臺(tái)通用計(jì)算機(jī)叫做ENIAC,這名字不是它的昵稱綽號(hào),就是它的功能,ENIAC的全稱為Electronic Numerical Integrator And Computer,即電子數(shù)字積分計(jì)算機(jī)),直到現(xiàn)在,計(jì)算機(jī)程序在底層硬件電路上仍然是0和1的計(jì)算問(wèn)題。

2.計(jì)算:計(jì)算機(jī)很笨,它其實(shí)只會(huì)計(jì)算0和1;但是人很聰明,人發(fā)現(xiàn)只要能把問(wèn)題轉(zhuǎn)換成0和1的運(yùn)算,就可以丟給計(jì)算機(jī)去處理了,然后,幾乎所有的問(wèn)題,都可以設(shè)法轉(zhuǎn)換成0和1的計(jì)算問(wèn)題。

3.程序:一次或幾次0和1的計(jì)算,幾乎不能解決任何問(wèn)題,需要很多次,步驟很復(fù)雜,過(guò)程很詳細(xì)的0和1的計(jì)算才行,這種專為計(jì)算機(jī)提供的復(fù)雜而詳細(xì)的計(jì)算步驟,就是計(jì)算機(jī)程序(為了向計(jì)算機(jī)傳遞程序,早期用打孔的紙帶,后來(lái)用磁帶,再后來(lái)用軟盤,再后來(lái)是硬盤、光盤、閃存什么的...)。

4.編程:編程就是編寫計(jì)算機(jī)程序,目的是把具體問(wèn)題轉(zhuǎn)換成0和1的運(yùn)算問(wèn)題,然后交給計(jì)算機(jī)去處理。

5.語(yǔ)言:編寫計(jì)算機(jī)程序是給計(jì)算機(jī)用的,所以早期用的都是機(jī)器語(yǔ)言(全是0和1)。這樣寫出來(lái)的程序全是0和1,人自己反而看不懂,所以就抽象出匯編語(yǔ)言,就像把英文翻譯成中文一樣,這樣人比較容易看懂。但是匯編語(yǔ)言描述的是底層電路的運(yùn)算過(guò)程(把數(shù)據(jù)從內(nèi)存的這里搬到那里,寄存器里的一個(gè)數(shù)據(jù)減去1,另一個(gè)數(shù)據(jù)乘以2),具體的輸入、輸出以及運(yùn)算的目的都很難識(shí)別出來(lái),所以又抽象出高級(jí)語(yǔ)言(C、BASIC等),不用再寫底層電路如何操作(高級(jí)語(yǔ)言需要先經(jīng)過(guò)編譯器生成對(duì)應(yīng)的匯編語(yǔ)言,再交給計(jì)算機(jī)去操作底層電路),只關(guān)心如何實(shí)現(xiàn)真實(shí)世界的業(yè)務(wù)邏輯。

編程語(yǔ)言和現(xiàn)實(shí)世界

6.抽象:編程的目的是把具體問(wèn)題轉(zhuǎn)成0和1的計(jì)算問(wèn)題,在高級(jí)語(yǔ)言里不用再考慮0和1了,我們可以更自由地把真實(shí)世界抽象為某種模型以便編寫代碼,這種抽象建模的過(guò)程,就是我們編程的核心能力

7.流派:關(guān)于如何對(duì)真實(shí)世界進(jìn)行抽象,是有不同流派的,面向?qū)ο笫呛兔嫦蜻^(guò)程對(duì)應(yīng)的,函數(shù)式編程是和命令式編程對(duì)應(yīng)的

8.面向過(guò)程和面向?qū)ο螅河?jì)算機(jī)的使命是用來(lái)計(jì)算,所有的計(jì)算都是有具體過(guò)程的,這樣就會(huì)很自然地把真實(shí)世界映射為計(jì)算的過(guò)程,對(duì)真實(shí)世界的建模就是直接建出一個(gè)個(gè)業(yè)務(wù)的流程,然后去運(yùn)轉(zhuǎn)而已。但是日益復(fù)雜的流程會(huì)變成一團(tuán)亂麻,難以理解,難以修復(fù),難以擴(kuò)展;

在面向?qū)ο笾?,不再糾結(jié)于流程本身,而是抽象出了對(duì)象的概念,把業(yè)務(wù)中的相關(guān)要素抽象為互相獨(dú)立又互相調(diào)用的對(duì)象,對(duì)象和對(duì)象之間的關(guān)系(繼承、封裝、多態(tài))成為核心,由于對(duì)象的概念更貼近人對(duì)于真實(shí)世界的理解,而且對(duì)象之間的關(guān)系也比整條復(fù)雜的流程簡(jiǎn)單,修改或者擴(kuò)展起來(lái)的波及范圍也小,容易理解/分解/修改/組合/擴(kuò)展,所以面向?qū)ο蠓浅_m合大型的軟件工程

面向過(guò)程和面向?qū)ο?/div>

9.命令式編程和函數(shù)式編程:換個(gè)角度來(lái)看,在計(jì)算機(jī)中實(shí)現(xiàn)業(yè)務(wù)邏輯有兩種書寫方式,一種是像輸入命令一樣,一步一步告訴計(jì)算機(jī)如何處理業(yè)務(wù)邏輯(還記得嗎,計(jì)算機(jī)很笨,只會(huì)做它懂的事情),這就是命令式編程。如果命令有誤,就是處理失敗,如果要修改業(yè)務(wù),就要把整個(gè)業(yè)務(wù)相關(guān)的命令都去檢查和修改一遍。

另一種是告訴計(jì)算機(jī),我需要什么,不去詳細(xì)地告訴它要怎么做,由于計(jì)算機(jī)不可能理解我們的需求,所以我們把函數(shù)拼接到一起,讓數(shù)據(jù)按照我們?cè)O(shè)想的方式流動(dòng),我們只要在數(shù)據(jù)流的最前面輸入?yún)?shù),等數(shù)據(jù)自己流完整個(gè)處理過(guò)程,就能得到我們需要的數(shù)據(jù)。如果數(shù)據(jù)有誤或者需要修改業(yè)務(wù),我們就去調(diào)整這個(gè)數(shù)據(jù)流,將它里面的數(shù)據(jù)流動(dòng)調(diào)整為我們需要的方式。

命令式和函數(shù)式

我們看到,函數(shù)式編程的運(yùn)算過(guò)程是高度抽象的,能節(jié)省大量運(yùn)算細(xì)節(jié)的代碼編寫和debug工作。

10.區(qū)別:面向?qū)ο蠛秃瘮?shù)式編程是有區(qū)別的,面向?qū)ο蟀颜鎸?shí)世界抽象為類和對(duì)象,函數(shù)式編程則把真實(shí)世界抽象為函數(shù);面向?qū)ο箨P(guān)心的是對(duì)象的行為,以及對(duì)象之間的關(guān)系,而函數(shù)式編程關(guān)心的是函數(shù)的行為,以及對(duì)函數(shù)的組合運(yùn)用;面向?qū)ο笾灰獙?duì)象不出錯(cuò),對(duì)象關(guān)系不出錯(cuò)就可以,函數(shù)式編程只要奔涌在函數(shù)組合里的數(shù)據(jù)流按照預(yù)期進(jìn)行轉(zhuǎn)換就可以。

11.選擇:在抽象建模的概念里,面向?qū)ο笠驗(yàn)橘N近真實(shí)世界,相對(duì)簡(jiǎn)單容易理解,工程上還容易擴(kuò)展維護(hù),所以很長(zhǎng)一段時(shí)間以來(lái),面向?qū)ο笤谲浖こ填I(lǐng)域備受歡迎。

12.現(xiàn)實(shí):從時(shí)間上來(lái)看,函數(shù)式編程其實(shí)并不新潮,但是過(guò)去主要活躍在大學(xué)和實(shí)驗(yàn)室里,這幾年突然變得火熱,背后一定有現(xiàn)實(shí)的原因。

13.硬件和并行:這些年來(lái),對(duì)計(jì)算機(jī)的應(yīng)用越來(lái)越廣泛,丟給計(jì)算機(jī)處理的問(wèn)題越來(lái)越多,計(jì)算量越來(lái)越大,所以計(jì)算機(jī)CPU就越來(lái)越快,一開(kāi)始還能每18個(gè)月翻一番(摩爾定律),到了這幾年單核CPU逼近物理極限,提升有限,就開(kāi)始著重搞多核,并行計(jì)算也越來(lái)越重要。

14.數(shù)據(jù)的問(wèn)題:計(jì)算機(jī)的本質(zhì)在于計(jì)算數(shù)據(jù),而軟件最大的問(wèn)題則是計(jì)算錯(cuò)誤(出bug),不巧的是,面向?qū)ο缶幊淘诓⑿杏?jì)算里就特別容易出現(xiàn)bug,因?yàn)樗暮诵氖歉鞣N獨(dú)立而又互相調(diào)用的對(duì)象,當(dāng)多個(gè)對(duì)象同時(shí)處理數(shù)據(jù)時(shí),就很容易導(dǎo)致數(shù)據(jù)修改的不確定性,從而引發(fā)bug。

15.混合:編程的本質(zhì)是把真實(shí)世界抽象映射到計(jì)算機(jī)的電路上,采用的抽象模式只是工具而已,我們沒(méi)有必要排斥函數(shù)式編程,也不需要放棄面向?qū)ο螅琄otlin也同時(shí)支持這兩種方式,我們需要的是根據(jù)需要選用工具,用錘子,用扳手,或者兩者都用。

要更深入地理解函數(shù)式編程,有一篇So You Want to be a Functional Programmer,寫的非常好,在函數(shù)式編程里,我們需要用到純函數(shù)、不變性、高階函數(shù)、閉包等概念。

純函數(shù)

開(kāi)發(fā)者在學(xué)習(xí)編程之前,其實(shí)都學(xué)過(guò)數(shù)學(xué),在數(shù)學(xué)的范疇里,函數(shù)的運(yùn)算是不受干擾的,比如你算一個(gè)數(shù)字的平方根,只要參數(shù)確定,計(jì)算的過(guò)程永遠(yuǎn)是一致的,算出來(lái)的結(jié)果永遠(yuǎn)是一樣的。

但是在學(xué)習(xí)編程(命令式編程)之后,函數(shù)就變了,變得“不純潔”了,函數(shù)的運(yùn)算會(huì)受到干擾,而且干擾無(wú)處不在,例如,我們可以在函數(shù)里使用一個(gè)會(huì)變化的全局變量,只要在任何位置/時(shí)間/線程里修改這個(gè)全局變量,函數(shù)就會(huì)輸出不同的結(jié)果。

函數(shù)會(huì)受到干擾

如果這種變化是開(kāi)發(fā)者故意設(shè)計(jì)的,開(kāi)發(fā)者就把它稱為業(yè)務(wù)邏輯;如果這種變化不符合開(kāi)發(fā)者的預(yù)期,開(kāi)發(fā)者就把它稱為——bug,悲劇的是,在命令式編程里,有無(wú)數(shù)的對(duì)象、時(shí)間點(diǎn)、線程可能對(duì)函數(shù)造成干擾。

在函數(shù)式編程里,重心是函數(shù)組合和數(shù)據(jù)流,更加不允許有干擾,所以要求我們編寫純函數(shù)。

不過(guò),純函數(shù)就像是編碼規(guī)范,Kotlin鼓勵(lì)而不是強(qiáng)制寫出函數(shù),畢竟,編程是為了與真實(shí)世界交互的,有時(shí)候必須使用一些“不純潔”的函數(shù),所以我們不要求徹底的純函數(shù)化,只要求盡量寫出純函數(shù)

不變性

函數(shù)式編程不僅要求純函數(shù),還要求保存不變性(Kotlin用val和集合表示不變性,是的,集合默認(rèn)是不可變的)

還是先回到數(shù)學(xué)上,在數(shù)學(xué)里,不允許這樣的表達(dá)(我在剛學(xué)編程時(shí),看到這個(gè)式子也是顛覆三觀的)

x = x + 1

在函數(shù)式編程里,這種表達(dá)也是非法的,也就是說(shuō),在函數(shù)式編程里,沒(méi)有可變變量,一個(gè)變量一旦被賦值,就不可更改。

不變性有很多好處,這意味著程序運(yùn)行的整個(gè)流程是固定可重現(xiàn)的,如果出了問(wèn)題,只要跟著數(shù)據(jù)流走一遍就能找到出錯(cuò)點(diǎn),再也不會(huì)有稀奇古怪的變化來(lái)為難我們。

不過(guò),不變性最大的好處在于多線程安全,它可以完美地規(guī)避多個(gè)線程同時(shí)修改一個(gè)數(shù)據(jù)時(shí)的同步問(wèn)題(變量不再允許修改,每個(gè)線程需要各自生成變量),這一點(diǎn)對(duì)于目前大量應(yīng)用多線程的工程現(xiàn)狀來(lái)說(shuō),特別有實(shí)際價(jià)值。

可是,如果變量不可變,我們還要怎樣去做業(yè)務(wù)邏輯呢,函數(shù)式編程給出的方式就是——用函數(shù)去返回一個(gè)復(fù)制的新對(duì)象,在這個(gè)新的對(duì)象里,改掉你想改的那個(gè)值。

更徹底地說(shuō),函數(shù)式編程里,沒(méi)有變量,一切都是函數(shù)(就像面向?qū)ο缶幊汤?,一切都是?duì)象),變量實(shí)際上被函數(shù)取代了

函數(shù)式編程里的變量也是函數(shù)

所以,函數(shù)式編程里只能新增變量,不能修改變量,所以函數(shù)式編程可能會(huì)非常耗內(nèi)存(生成的變量太多了,而且業(yè)務(wù)不走完,變量不釋放)

另外,在函數(shù)式編程里還有一個(gè)特點(diǎn)——沒(méi)有循環(huán),因?yàn)閒or(i: i<9;i++)是非法的(當(dāng)然,在Kotlin里你還可以這樣寫,因?yàn)镵otlin既支持函數(shù)式編程,又支持面向?qū)ο螅?/p>

高階函數(shù)

既然變量已經(jīng)被函數(shù)取代了,那么函數(shù)里的參數(shù)和返回值呢?這些對(duì)象是不是也可以被替換成為函數(shù)呢?

在面向函數(shù)編程里,有個(gè)重要的概念,叫做“函數(shù)是一等公民”,核心就是,函數(shù)擁有和數(shù)據(jù)一樣的地位,都可以作為參數(shù)和返回值,相應(yīng)的就出現(xiàn)了高階函數(shù)的概念,簡(jiǎn)單理解,高階函數(shù)就是參數(shù)為函數(shù),或者返回值為函數(shù)的函數(shù)。

可以把函數(shù)作為輸入輸出

我們知道,在開(kāi)發(fā)過(guò)程中,復(fù)用是非常重要的優(yōu)化手段,說(shuō)白了,能用1個(gè)函數(shù)就別用多個(gè)函數(shù),不容易出錯(cuò),出錯(cuò)也容易檢查和修改

那么我們看下面這兩個(gè)函數(shù),要怎么優(yōu)化?

fun getA(){

doA()

}

fun getB(){

doB()

}

在面向?qū)ο缶幊汤铮覀兊谝环磻?yīng)是用接口和類來(lái)解決問(wèn)題,當(dāng)然,那樣就得好幾個(gè)類和接口,然后層層嵌套

有了高階函數(shù)的話,開(kāi)頭那段代碼就可以這樣優(yōu)化了

fun getAB(doA()){

}

(在Kotlin里不能直接這么寫,需要用Lambda表達(dá)式才行)

在Kotlin里,lambda還可以作為一種類型,可以被定義為val

一個(gè)lambda的類型

調(diào)用這個(gè)lambda類型的“對(duì)象”,與調(diào)用函數(shù)無(wú)異

調(diào)用

閉包

前面說(shuō)過(guò),函數(shù)式編程里的函數(shù)是第一等公民,所以,一個(gè)val可以是一段代碼,這就是一個(gè)閉包

一個(gè)閉包

不過(guò),閉包不是函數(shù),閉包在邏輯上是封閉的,它使用自己內(nèi)部的數(shù)據(jù),用自己內(nèi)部的邏輯進(jìn)行處理,外部只能得到閉包的輸出,無(wú)法輸入,也無(wú)法干擾。

在系統(tǒng)資源上,閉包是持久使用的,它會(huì)一直在系統(tǒng)里,不像函數(shù)那樣會(huì)被系統(tǒng)注銷掉。

閉包在函數(shù)式編程里可以簡(jiǎn)化參數(shù)量、減少變量,會(huì)更加方便我們的開(kāi)發(fā)。

其他

另外,函數(shù)式編程還有柯里化、inline、with、apply、let、run、it等概念,我們以后可以慢慢了解。

接下來(lái),我們看看Kotlin里支撐起函數(shù)式編程的Lambda表達(dá)式、流式API等特性。

Lambda表達(dá)式

為了寫高階函數(shù)和閉包,Kotlin支持我們使用Lambda表達(dá)式。

Lambda表達(dá)式也叫λ表達(dá)式,它看起來(lái)就是對(duì)匿名方法(如:回調(diào)、事件響應(yīng)、Runnable等)的簡(jiǎn)化寫法,目的是為了更貼近函數(shù)式編程把函數(shù)作為參數(shù)的思想。

Lambda表達(dá)式包括最外面的“{}”,用“()”來(lái)定義的參數(shù)列表,箭頭->,以及一個(gè)表達(dá)式或語(yǔ)句塊。

事件響應(yīng)的簡(jiǎn)化:

textView.setOnClickListener(newOnClickListener(){

? ?@Override

? ?public void onClick(View view){//todo}

? ?}

);

簡(jiǎn)化為

textView.setOnClickListener{/*todo*/}

Runnable的簡(jiǎn)化:

executor.submit(

? ?newRunnable(){

? ? ?@Override

? ? ?public void run(){

? ? ? ? ?//todo

? ? ?}

? }

);

簡(jiǎn)化為:

executor.submit({//todo })

使用lambda表達(dá)式,我們就可以編寫高階函數(shù),傳遞一個(gè)函數(shù)(或者一段代碼)作為參數(shù)。

流式(Stream)API

前面提過(guò),函數(shù)式編程以數(shù)據(jù)流為中心,通過(guò)組合函數(shù)來(lái)整理一個(gè)數(shù)據(jù)流,通過(guò)調(diào)整這個(gè)函數(shù)組合得出需要的數(shù)據(jù)。

要讓數(shù)據(jù)流在組合函數(shù)里流動(dòng)起來(lái),就需要使用流式API,流式API使我們更容易把函數(shù)組合起來(lái),而且使整個(gè)數(shù)據(jù)流動(dòng)過(guò)程更加直觀。

如果接觸過(guò)Java8或者RxAndroid,應(yīng)該很容易理解流式API,我以前寫過(guò)RxAndroid使用初探—簡(jiǎn)潔、優(yōu)雅、高效,感興趣可以去讀一下,流式API寫出來(lái)的代碼風(fēng)格如下

流式API的代碼風(fēng)格

一些有趣的函數(shù)

Kotlin里提供了一些有趣的函數(shù),包括it,let,apply,run,with,inline等

1.it

我們知道,用lambda表達(dá)式,我們可以把一些函數(shù)的寫法簡(jiǎn)化成“輸入?yún)?shù)->(運(yùn)算)輸出”,其中,如果只有一個(gè)參數(shù)時(shí),寫出來(lái)的代碼就像是

val dints=ints.map{value->value*2}

對(duì)于這種單個(gè)參數(shù)的運(yùn)算式,可以進(jìn)一步簡(jiǎn)化,把參數(shù)聲明和->都簡(jiǎn)化掉,只保留運(yùn)算輸出,不過(guò)這要用it來(lái)統(tǒng)一代替參數(shù),代碼就變成

val dints2=ints.map{ it*2}

這就是it的用法,進(jìn)一步簡(jiǎn)化單參數(shù)的lambda表達(dá)式。

2.let

let能把更復(fù)雜的對(duì)象賦給it,比如

File("a.text").let{

? ? it.absoluteFile ?//let把file對(duì)象賦給了it

}

這個(gè)特性可以稍微擴(kuò)展一下,比如增加?檢查

getVaraiable()?.let{

? ? it.length? ? // when not null

}

這樣可以先檢查返回值是否為空,不為空才繼續(xù)進(jìn)行

3.apply

apply可以操作一個(gè)對(duì)象的任意函數(shù),再結(jié)合let返回該對(duì)象,例如

ints.apply{//拿到一個(gè)arraylist對(duì)象

? ? add(0,3) ?//操作該對(duì)象的函數(shù)

}.let{ it.size} // 返回該對(duì)象(已被修改),繼續(xù)處理

4.run

apply是操作一個(gè)對(duì)象,run則是操作一塊兒代碼

apply返回操作的對(duì)象,run的返回則是最后一行代碼的對(duì)象

ints.run(){ //操作一個(gè)集合

add(0,3) //操作該集合

var a=Activity()

a //會(huì)返回最后一行的對(duì)象

}.let{ it.actionBar}

5.with

with有點(diǎn)兒像apply,也是操作一個(gè)對(duì)象,不過(guò)它是用函數(shù)方式,把對(duì)象作為參數(shù)傳入with函數(shù),然后在代碼塊中操作,例如

with(ints){ //傳入一個(gè)集合

? ? add(0,3) //操作該集合

? ? var a=Activity()

? ? a //會(huì)返回最后一行的對(duì)象

}.let{ it.actionBar}

但是返回像run,也是最后一行

6.inline

inline內(nèi)聯(lián)函數(shù),其實(shí)相當(dāng)于對(duì)代碼塊的一個(gè)標(biāo)記,這個(gè)代碼塊將在編譯時(shí)被放進(jìn)代碼的內(nèi)部,相當(dāng)于說(shuō),內(nèi)聯(lián)函數(shù)在編譯后就被打散到調(diào)用它的函數(shù)里的,目的是得到一些性能上的優(yōu)勢(shì)。

Kotlin的潛在問(wèn)題

Kotlin也有一些潛在的問(wèn)題是我們需要注意的,主要是開(kāi)發(fā)時(shí)容易遇到的一些問(wèn)題。

思維方式的問(wèn)題

我們已經(jīng)知道Kotlin的核心在于函數(shù)式編程,問(wèn)題在于函數(shù)式編程的核心不是語(yǔ)法的問(wèn)題,而是思維方式的問(wèn)題,語(yǔ)法容易轉(zhuǎn)變,思維卻很難,所以沒(méi)有函數(shù)式編程經(jīng)驗(yàn)的話,切換到Kotlin其實(shí)會(huì)相當(dāng)困難。

Kotlin->Java的轉(zhuǎn)換

我們應(yīng)該注意,AS中只提供了從Java文件轉(zhuǎn)換為Kotlin文件的工具,并沒(méi)有逆向轉(zhuǎn)換的工具,就是說(shuō)目前你還不能很輕松地把Kotlin代碼轉(zhuǎn)換為Java代碼,一件事情如果不能回退,就必須小心謹(jǐn)慎。

團(tuán)隊(duì)開(kāi)發(fā)的問(wèn)題

一般來(lái)說(shuō),鑒于Kotlin和Java兼容良好,可以一邊維持舊的Java代碼,一邊開(kāi)發(fā)新的Kotlin代碼和新的Java代碼,但是團(tuán)隊(duì)開(kāi)發(fā)不僅是兼容性的問(wèn)題,Kotlin語(yǔ)法糖背后的很多思維方式也許會(huì)對(duì)團(tuán)隊(duì)造成沖擊,例如,一旦某個(gè)模塊采用了流式API的話,其他團(tuán)隊(duì)成員在調(diào)用這個(gè)模塊時(shí),也需要理解并且能夠編寫流式API才能完成工作銜接,這就可能帶來(lái)額外的成本和意外的延期。

最后,簡(jiǎn)單介紹一下怎樣開(kāi)始在AS中使用Kotlin語(yǔ)言。

在AS中使用Kotlin語(yǔ)言

Android Studio對(duì)Kotlin的支持非常友好(畢竟算是同門),我們先簡(jiǎn)單地看一下怎樣安裝和使用Kotlin(AS版本2.2.3),再來(lái)體會(huì)Kotlin在編程上的優(yōu)勢(shì)。

1.安裝

插件

打開(kāi)settings-plugins-install JetBrains plugin...

點(diǎn)擊“Install JetBrains Plugin...”,然后搜索kotlin。

搜索

搜索并安裝kotlin

安裝

下載

Kotlin安裝中

重啟AS

重啟

重啟AS

2.使用

創(chuàng)建項(xiàng)目:沒(méi)有變化。

創(chuàng)建Activity:增加了Kotlin Activity的選項(xiàng)。

activity

增加了Kotlin Activity

創(chuàng)建類/文件:增加了Kotlin文件/類的選項(xiàng),同上圖。

Kotlin的文件類型在右下角都有個(gè)“K”字形的角標(biāo)。

文件

Kotlin文件

初次創(chuàng)建時(shí)會(huì)提示需要進(jìn)行配置,實(shí)際就是告訴編譯器,這個(gè)module用kotlin編譯還是用java編譯。

配置

提示配置Kotlin

Kotlin和Java可以無(wú)縫兼容,但是需要你通過(guò)配置,說(shuō)明哪些module是Kotlin的,哪些module是Java的。

選擇編譯語(yǔ)言

選擇哪些module是Kotlin的

在project的gradule里增加了kotlin version和dependencies的引用

gradule

project的gradule設(shè)置

在app的gradule里增加了關(guān)于Kotlin的app plugin和dependencies

app graduate

app的gradule設(shè)置

針對(duì)已經(jīng)存在的Java文件,可以轉(zhuǎn)換為Kotlin文件

convert

轉(zhuǎn)換文件

Kotlin文件的后綴名不再是.java,而是.kt

后綴名

文件擴(kuò)展名為kt。

現(xiàn)在,我們可以編寫Kotlin代碼了。

參考

Kotlin官方文檔

Using Project Kotlin for Android

Swift is like Kotlin

Kotlin相對(duì)于Java的優(yōu)勢(shì)比較

為什么我要改用Kotlin

用 Kotlin 寫 Android ,難道只有環(huán)境搭建這么簡(jiǎn)單?

用 Kotlin 寫 Android 02 說(shuō)說(shuō) Anko

使用Kotlin&Anko, 扔掉XML開(kāi)發(fā)Android應(yīng)用

400% faster layouts with Anko

NullPointException 利器 Kotlin 可選型

Data Classes in Kotlin: save a good bunch of lines of code (KAD 10)

函數(shù)式編程掃盲篇

So You Want to be a Functional Programmer

進(jìn)行Kotlin實(shí)戰(zhàn)開(kāi)發(fā)前,你應(yīng)了解的那些技術(shù)點(diǎn)

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

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

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