Android必備技能之一:Kotlin(一)

20161105_161848.jpg

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(下面簡(jiǎn)稱AS)環(huán)境上面操作,雖然使用IDE IntelliJ也可以實(shí)現(xiàn),但AS上也是對(duì)Kotlin支持的,首先下載以下相關(guān)插件(雖然并不是所有插件一次性用到,但建議一次性下載完,后續(xù)就不需要下載了):

  • Kotlin
  • Kotlin Extensions For Android
  • Anko DSL Preview

其中Anko DSL Preview插件用于預(yù)覽使用DSL編寫的UI代碼,就像以前使用xml編寫UI文件時(shí)可以動(dòng)態(tài)在“Preview”窗口預(yù)覽效果一樣。

上面三個(gè)插件下載安裝重啟之后,然后新建一個(gè)項(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對(duì)android的擴(kuò)展,同時(shí)也添加了Kotlin的gradle插件。我們打開系統(tǒng)默認(rèn)幫我們建的MainActivity,然后Code->Convert Java File to Kotlin File->OK,此時(shí)我們?cè)械腁ctivity應(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)可以跑起來了。你可能要說代碼相比之下并沒有簡(jiǎn)潔多少啊 ,當(dāng)然到這里并沒有結(jié)束,反而是開始,只是首先一起來體驗(yàn)下我們的第一個(gè)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í)就是對(duì)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)庫更多的是對(duì)Java庫的擴(kuò)展,基于這個(gè)設(shè)計(jì)思路,你絲毫不需要擔(dān)心Kotlin對(duì)Java代碼的引用,你甚至可以在Kotlin當(dāng)中使用Java反射,反正只要是Java有的,Kotlin都有。最常見的就是Getter/Setter方法對(duì)應(yīng)到Kotlin屬性的調(diào)用,ex如下:
首先準(zhǔn)備一個(gè)java類:

8208E5B2-6AA0-4F0F-BA2D-A1F067D08DCC.png

調(diào)用如下:

       val javaPerson = Person()
        javaPerson.address = "Wo shi Kotlin"
        debug(javaPerson.address)

打印結(jié)果就是"Wo shi Kotlin"了,剛剛只顧著寫,忘了提一句,在Kotlin語法里面,是不用在后面加“;”的,有沒有感覺很爽,廢話不說了,可以看到我們調(diào)用時(shí)候,既沒有用到get方法也沒有用到set方法,是不是避免了總會(huì)有人說get/set方法影響性能的問題。
官網(wǎng)地址(有詳細(xì)的比較):https://kotlinlang.org/docs/reference/java-interop.html#static-methods-and-fields

4、簡(jiǎn)潔 可靠 有趣

4.1數(shù)據(jù)類

在JavaBean中我們往往會(huì)覆寫諸如equals和hashcode等方法,一旦用到HashMap這樣的集合框架,總是出了問題都不知道找誰。Kotlin提供了一種非常簡(jiǎn)單的方式來創(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就會(huì)創(chuàng)建出一個(gè)完整的數(shù)據(jù)類,并自動(dòng)生成相應(yīng)的equals、hashcode、toString方法。

4.2 空安全與屬性代理

想想平時(shí)QA提的bug,不太靠譜的server,不太確定數(shù)據(jù)類型,均可能出現(xiàn)Exception,但我們總不能在所有地方都進(jìn)行判斷,第一次看到Kotlin的空安全處理,真的眼前一亮。
Kotlin的空安全設(shè)計(jì),主要是在類型后面加?表示可空,否則就不能為null,ex如下:

val nullable: Int? = 0
val nonNullable: Int = 2
nullable.toFloat() // 編譯錯(cuò)誤
nullable?.toFloat() // 如果null,什么都不做,否則調(diào)用toFloat
nullable!!.toFloat() // 強(qiáng)制轉(zhuǎn)換為非空對(duì)象,并調(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場(chǎng)景使用

好了,接下來我們就實(shí)戰(zhàn)說說Kotlin的用法,用如下代碼舉例:
java代碼:

8F167006-EA0F-4D14-BE9B-20BA6562DDEA.png

Kotlin代碼:

46EA6401-2A70-40FA-91A5-9EAA38E76670.png

5.1 場(chǎng)景一(控件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寫法還簡(jiǎn)單一點(diǎn),并且設(shè)置text和size時(shí)候,需要加兩個(gè)嘆號(hào),不加的話貌似編譯器并不識(shí)別你是否為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 {}

這種寫法好像簡(jiǎn)單了一點(diǎn),不過好像依然很復(fù)雜,注意在這里初始化的時(shí)候不要直接

private var tv_hello_view: TextView  //編譯錯(cuò)誤

lazy是Kotlin的屬性代理的一個(gè)實(shí)例,它提供了延遲加載的機(jī)制。換句話說,這里的lazy提供了初始化aTextView的方法,不過真正初始化這個(gè)動(dòng)作發(fā)生的時(shí)機(jī)卻是在aTextView第一次被使用時(shí)了。lazy默認(rèn)是線程安全的,你當(dāng)然也可以關(guān)掉這個(gè)配置,只需要加個(gè)參數(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 來修飾它,方法簡(jiǎn)單了不少吧 ,但是findViewById這個(gè)單詞好長啊,能不能簡(jiǎn)化啊,答案是肯定的,我們請(qǐng)出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。既然請(qǐng)出來了Anko ,那么我們還有終極方案,完全去除findViewById。

寫法五:

CD9648CF-79D0-48FB-AED7-59A2CAAF448B.png

可以發(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所牽扯出來的幾個(gè)點(diǎn):lazy,primitives, lateinit, Anko以及不要 findViewById仍然能找到控件的原理我并沒有去細(xì)致分析,后續(xù)有時(shí)間我會(huì)再補(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

最后編輯于
?著作權(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)容