1、Kotlin前世與今生
- 寫了太久Java,有沒有發(fā)現(xiàn)其實(shí)你寫了太多冗余的代碼?
- 你雖然小心翼翼,可總是被QA折騰出來的NullPointerException所頭疼,難道就沒有受夠這種日子么?
- 直到有一天你發(fā)現(xiàn)自己代碼除了if,else,for循環(huán),竟然沒有任何留戀?
那么我們可以一起來嘗試一下 Kotlin,話說回來了什么是Kotlin呢?
Kotlin是基于JVM新的編程語言,由 JetBrains 開發(fā),可以編譯成java字節(jié)碼,也可以編譯成JavaScript。而JetBrains,作為目前廣受歡迎的Java IDE IntelliJ 的提供商,也在 Apache 許可下已經(jīng)開源其Kotlin 編程語言。
2、Kotlin環(huán)境配置
接下來的我就直接在Android Studio(下面簡稱AS)環(huán)境上面操作,雖然使用IDE IntelliJ也可以實(shí)現(xiàn),但AS上也是對Kotlin支持的,首先下載以下相關(guān)插件(雖然并不是所有插件一次性用到,但建議一次性下載完,后續(xù)就不需要下載了):
- Kotlin
- Kotlin Extensions For Android
- Anko DSL Preview
其中Anko DSL Preview插件用于預(yù)覽使用DSL編寫的UI代碼,就像以前使用xml編寫UI文件時可以動態(tài)在“Preview”窗口預(yù)覽效果一樣。
上面三個插件下載安裝重啟之后,然后新建一個項(xiàng)目默認(rèn)配置Gradle如下:
ext.kotlin_version = '1.1.0'
ext.anko_version = '0.8.2'
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
.........................
dependencies {
.........................
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "org.jetbrains.anko:anko-sdk15:$anko_version"
compile "org.jetbrains.anko:anko-support-v4:$anko_version"
.........................
}
repositories {
mavenCentral()
}
這里添加了Kotlin對android的擴(kuò)展,同時也添加了Kotlin的gradle插件。我們打開系統(tǒng)默認(rèn)幫我們建的MainActivity,然后Code->Convert Java File to Kotlin File->OK,此時我們原有的Activity應(yīng)該將我們轉(zhuǎn)換成了如下內(nèi)容:
class KoTlinActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ko_tlin)
}
}
上面這一步是將java代碼轉(zhuǎn)換為Kotlin代碼。截止到現(xiàn)在,你什么都不用做,程序就已經(jīng)可以跑起來了。你可能要說代碼相比之下并沒有簡潔多少啊 ,當(dāng)然到這里并沒有結(jié)束,反而是開始,只是首先一起來體驗(yàn)下我們的第一個Kotlin代碼程序。
3、Kotlin完美給Java開發(fā)者打造
3.1 通用的集合框架和Kotlin的擴(kuò)展
通用的集合框架,ex如下:
val list = arrayListOf(1, 2, 3, 4)
list.add(5)
list.remove(3)
for (item in list) {
debug(item);
}
效果如下:
03-09 16:06:23.991 21108-21108/com.Igeek.kotlin D/MainActivity: 1
03-09 16:06:23.991 21108-21108/com.Igeek.kotlin D/MainActivity: 2
03-09 16:06:23.991 21108-21108/com.Igeek.kotlin D/MainActivity: 4
03-09 16:06:23.991 21108-21108/com.Igeek.kotlin D/MainActivity: 5
可以看到這里Kotlin寫法和java是差不多的。
至于Kotlin的擴(kuò)展,其實(shí)就是對java的庫進(jìn)行了進(jìn)一步擴(kuò)展,ex如下:
val list = arrayListOf(1, 2, 3, 4, 5)
list.forEach {
debug(it)
}
debug("==========================")
val doubleList = list.map {
it * 2
}
doubleList.forEach {
debug(it)
}
debug("==========================")
val oddList = list.filter{
it % 2 == 1
}
oddList.forEach {
debug(it)
}
打印如下:
03-09 16:18:06.978 28828-28828/com.Igeek.kotlin D/MainActivity: 1
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 2
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 3
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 4
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 5
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: ==========================
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 2
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 4
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 6
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 8
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 10
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: ==========================
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 1
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 3
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 5
3.1 Kotlin與Java的交互
Kotlin的標(biāo)準(zhǔn)庫更多的是對Java庫的擴(kuò)展,基于這個設(shè)計思路,你絲毫不需要擔(dān)心Kotlin對Java代碼的引用,你甚至可以在Kotlin當(dāng)中使用Java反射,反正只要是Java有的,Kotlin都有。最常見的就是Getter/Setter方法對應(yīng)到Kotlin屬性的調(diào)用,ex如下:
首先準(zhǔn)備一個java類:
調(diào)用如下:
val javaPerson = Person()
javaPerson.address = "Wo shi Kotlin"
debug(javaPerson.address)
打印結(jié)果就是"Wo shi Kotlin"了,剛剛只顧著寫,忘了提一句,在Kotlin語法里面,是不用在后面加“;”的,有沒有感覺很爽,廢話不說了,可以看到我們調(diào)用時候,既沒有用到get方法也沒有用到set方法,是不是避免了總會有人說get/set方法影響性能的問題。
官網(wǎng)地址(有詳細(xì)的比較):https://kotlinlang.org/docs/reference/java-interop.html#static-methods-and-fields
4、簡潔 可靠 有趣
4.1數(shù)據(jù)類
在JavaBean中我們往往會覆寫諸如equals和hashcode等方法,一旦用到HashMap這樣的集合框架,總是出了問題都不知道找誰。Kotlin提供了一種非常簡單的方式來創(chuàng)建這樣的數(shù)據(jù)類,ex如下:
data class Main(val id:Int,val name:String)
//main方法調(diào)用
fun main(args:Array<out String>){
println(Main(0,"Main函數(shù)"))
}
打印結(jié)果:
Main(id=0, name=Main函數(shù))
僅僅一行代碼,Kotlin就會創(chuàng)建出一個完整的數(shù)據(jù)類,并自動生成相應(yīng)的equals、hashcode、toString方法。
4.2 空安全與屬性代理
想想平時QA提的bug,不太靠譜的server,不太確定數(shù)據(jù)類型,均可能出現(xiàn)Exception,但我們總不能在所有地方都進(jìn)行判斷,第一次看到Kotlin的空安全處理,真的眼前一亮。
Kotlin的空安全設(shè)計,主要是在類型后面加?表示可空,否則就不能為null,ex如下:
val nullable: Int? = 0
val nonNullable: Int = 2
nullable.toFloat() // 編譯錯誤
nullable?.toFloat() // 如果null,什么都不做,否則調(diào)用toFloat
nullable!!.toFloat() // 強(qiáng)制轉(zhuǎn)換為非空對象,并調(diào)用toFloat;如果nullable為null,拋空指針異常
nonNullable.toFloat() // 正確
我們利用Convert Java File to Kotlin File生成的Kotlin代碼,在onCreate方法中也是如此考慮,savedInstanceState是否為空。
override fun onCreate(savedInstanceState: Bundle?) {}
注:這里的空指針異常是KotlinNullPointerException,而不是Java的NullPointerException。
5、Kotlin場景使用
好了,接下來我們就實(shí)戰(zhàn)說說Kotlin的用法,用如下代碼舉例:
java代碼:
Kotlin代碼:
5.1 場景一(控件findViewById)
findViewById有很多寫法,我們就從復(fù)雜到容易說起:
寫法一:
private var tv_hello_view: TextView? = null
.......................................
tv_hello_view = findViewById(R.id.tv_hello_view) as TextView
tv_hello_view!!.text = "Say Hello!!!"
tv_hello_view!!.textSize = 22f
tv_hello_view!!.setOnClickListener {}
有沒有發(fā)現(xiàn)寫法一還不如java寫法,貌似java寫法還簡單一點(diǎn),并且設(shè)置text和size時候,需要加兩個嘆號,不加的話貌似編譯器并不識別你是否為null,下面我們來看寫法二:
寫法二:
private val tv_hello_view: TextView by lazy{
findViewById(R.id.tv_hello_view) as TextView
}
.......................................
tv_hello_view.text = "Say Hello!!!"
tv_hello_view.textSize = 22f
tv_hello_view.setOnClickListener {}
這種寫法好像簡單了一點(diǎn),不過好像依然很復(fù)雜,注意在這里初始化的時候不要直接
private var tv_hello_view: TextView //編譯錯誤
lazy是Kotlin的屬性代理的一個實(shí)例,它提供了延遲加載的機(jī)制。換句話說,這里的lazy提供了初始化aTextView的方法,不過真正初始化這個動作發(fā)生的時機(jī)卻是在aTextView第一次被使用時了。lazy默認(rèn)是線程安全的,你當(dāng)然也可以關(guān)掉這個配置,只需要加個參數(shù)LazyThreadSafetyMode.NONE即可:
private val tv_hello_view: TextView by lazy(LazyThreadSafetyMode.NONE){
findViewById(R.id.tv_hello_view) as TextView
}
寫法三:
private lateinit var tv_hello_view: TextView
.......................................
tv_hello_view = findViewById(R.id.tv_hello_view) as TextView
tv_hello_view.text = "Say Hello!!!"
tv_hello_view.textSize = 22f
tv_hello_view.setOnClickListener {}
這里主要用了lateinit 來修飾它,方法簡單了不少吧 ,但是findViewById這個單詞好長啊,能不能簡化啊,答案是肯定的,我們請出Anko,注意我們有依賴過dependencies喲。
寫法四:
private lateinit var tv_hello_view: TextView
.......................................
tv_hello_view = find(R.id.tv_hello_view)
tv_hello_view.text = "Say Hello!!!"
tv_hello_view.textSize = 22f
tv_hello_view.setOnClickListener {}
可以看到我們方法改成了find,并且沒了 as TextView,注意我們需要import org.jetbrains.anko.find。既然請出來了Anko ,那么我們還有終極方案,完全去除findViewById。
寫法五:
可以發(fā)現(xiàn)直接操作tv_hello_view,這便是findViewById的終極寫法。
注:
1.導(dǎo)入了import kotlinx.android.synthetic.main.hello_layout.*包
2. tv_hello_view是hello_layout布局xml中的id
寫到這里,內(nèi)容也挺多了,我也只是把用法統(tǒng)一歸納一下,至于findViewById所牽扯出來的幾個點(diǎn):lazy,primitives, lateinit, Anko以及不要 findViewById仍然能找到控件的原理我并沒有去細(xì)致分析,后續(xù)有時間我會再補(bǔ)充上,當(dāng)然如果想更多的去了解,可以有一下資料參考:
Kotlin官網(wǎng)文檔地址:https://kotlinlang.org/docs/
《Kotlin for android Developers》中文翻譯: https://github.com/wangjiegulu/kotlin-for-android-developers-zh/blob/master/SUMMARY.md
在這里推薦一部Kotlin基礎(chǔ)學(xué)習(xí)視頻:https://pan.baidu.com/s/1b2tBH0 提取碼:nryk
本博客參考了一下文獻(xiàn):
Kotlin for android Developers》中文翻譯;
博客:http://www.println.net/post/Android-A-Powerful-Substitution-Kotlin;
博客:http://mp.weixin.qq.com/s?__biz=MzIzMTYzOTYzNA==&mid=100000121&idx=1&sn=6a8c4b27dec4e03a58e888c5fa18b7e2&chksm=68a05e445fd7d752da50717bec037f51702aa9557b308114f2d7255109509bcd24c1a5d80903&mpshare=1&scene=23&srcid=0309PegdxPNifENVckrvhBZY#rd