Kotlin開發(fā)總結

綱要


  1. 前言
  2. Kotlin是什么?
  3. 為什么使用Kotlin?
  4. Kotlin常用特性有哪些(與Java比較)?
  5. 如何在開發(fā)中集成Kotlin?
  6. 使用Kotlin常見問題有哪些?

前言


從2017年秋季接觸Kotlin以來,開始3個月寫了一些學習Demo,最近半年才開始用Kotlin開發(fā)企業(yè)級的APP,遇到了一點坑,但整體感覺:簡潔、高效,以至于現(xiàn)在絲毫不想寫又臭又長的Java代碼。
還在堅持寫Java的同學不妨試試Kotlin,一定會讓你流連忘返、樂不思Java ,:p。

Kotlin是什么?


簡介

Kotlin是一種在Java虛擬機上運行的靜態(tài)類型編程語言,它也可以被編譯成為JavaScript源代碼。
主要由JetBrains的圣彼得堡團隊所開發(fā),其名稱來自于圣彼得堡附近的科特林島。

靜態(tài)動態(tài)類型編程語言:通俗講
靜態(tài)類型指的是編譯器在compile time執(zhí)行類型檢查,動態(tài)類型指的是編譯器(虛擬機)在runtime執(zhí)行類型檢查

JVM語言 Kotlin vs Java

Kotlin和Java源文件都需要先編譯成符合JVM規(guī)范的字節(jié)碼文件,才能夠在JVM運行。因此,本質(zhì)上講

  1. Java能做的,Kotlin都能做,反之亦然。
  2. Java和Kotlin能夠相互轉(zhuǎn)換。通過字節(jié)碼文件可以反編譯為對方的源碼文件。
Kotlin和Java編譯流程對比

應用范圍

適用構建Kotlin應用的平臺(引自官網(wǎng))

JVM-Java服務端開發(fā)(替代Java)
Android
Browser-JavaScript開發(fā)(替代JavaScript)
Native-C相關開發(fā)(替代C、Object-C和Swift)

歷史

Kotlin歷史版本

  • 2011年7月-JetBrains推出Kotlin項目
  • 2016年2月-發(fā)布v1.0版本
  • 2017年11月28日-v1.2
  • 2018年6月14日-v1.2.50

Google支持情況

為什么使用Kotlin?


通用理由

引自官網(wǎng)中文站

參考示例見官方
https://www.kotlincn.net/

使用Kotlin開發(fā)Android的理由

  • 兼容性:Kotlin 與 JDK 6 完全兼容,保障了 Kotlin 應用程序可以在較舊的 Android 設備上運行而無任何問題。Kotlin 工具在 Android Studio 中會完全支持,并且兼容 Android 構建系統(tǒng)。
  • 性能:由于非常相似的字節(jié)碼結構,Kotlin 應用程序的運行速度與 Java 類似。 隨著 Kotlin 對內(nèi)聯(lián)函數(shù)的支持,使用 lambda 表達式的代碼通常比用 Java 寫的代碼運行得更快。
  • 互操作性:Kotlin 可與 Java 進行 100% 的互操作,允許在 Kotlin 應用程序中使用所有現(xiàn)有的 Android 庫 。這包括注解處理,所以數(shù)據(jù)綁定和 Dagger 也是一樣。
  • 占用:Kotlin 具有非常緊湊的運行時庫,可以通過使用 ProGuard 進一步減少。 在實際應用程序中,Kotlin 運行時只增加幾百個方法以及 .apk 文件不到 100K 大小。
  • 編譯時長:Kotlin 支持高效的增量編譯,所以對于清理構建會有額外的開銷,增量構建通常與 Java 一樣快或者更快。
  • 學習曲線:對于 Java 開發(fā)人員,Kotlin 入門很容易。包含在 Kotlin 插件中的自動 Java 到 Kotlin 的轉(zhuǎn)換器有助于邁出第一步。Kotlin 心印 通過一系列互動練習提供了語言主要功能的指南。

另外的理由

  1. 不用寫煩人的findViewById
  2. Android Studio支持Java To Kotlin一鍵轉(zhuǎn)換

Kotlin常用特性有哪些(與Java比較)?


具體語法可見Kotlin官網(wǎng),或對應的中文版
以下主要說一下常用的語法或特性

基本語法

定義變量
局部變量-kotlin

    fun testDefination() {
        // 不可變
        val a: Int = 1  // 立即賦值
        val b = 2   // 自動推斷出 `Int` 類型
        val c: Int  // 如果沒有初始值類型不能省略
        c = 3       // 明確賦值

        // 可變
        var x = 5 // 自動推斷出 `Int` 類型
        x += 1
    }

局部變量-java

    public void testDefination() {
        final int a = 1;// 可變
        int b = 1;// 不可變
    }

成員變量的懶加載和延遲加載-kotlin

    // 懶加載
    private val freshman by lazy {
        Person("feifei", 18, true)
    }
    // 延遲加載。定義成員變量時必須初始化,但不想初始化或不想引入空時,可以使用延遲加載
    private lateinit var old: Person

成員變量的懶加載和延遲加載-java?

字符串模板
kotlin

    @Test fun testStringTemplates() {
        val age = 12
        val name = "feifei"
        print("name -> $name, age -> $age")
    }

java

    public void testPrintString() {
        String name = "feifei";
        int age = 18;
        System.out.print("name -> " + name + ",age ->" + age);
    }

Elvis 操作符?

    // 判空。如果str=null,會打?。篖ength = null
    @Test fun testNull (str: String?) {
        println("Length = " + str?.length)
    }

    // 類型轉(zhuǎn)換
    @Test fun testCast() {
        val l2 = listOf("A",1,3,6,8,'c')
        l2.forEach { println(it as? Int) }
    }

斷言操作符!!

    private var p1: Person? = null
    @Test
    fun testAssert() {
        p1 = Person("feifei", 18, true)
        println(p1!!.name)
    }

條件表達式

    @Test
    fun testCondition() {
        // 空條件
        val files = File("Test").listFiles()
        println(files?.size ?: "empty")

        // if-else
        println("max -> ${maxOf(1,2)}")
        
        // when
        val items = listOf("apple", "banana", "kiwifruit", "grape", "pear", "ab...")
        when {
            "orange" in items -> println("juicy")
            "apple" in items -> println("apple is fine too")
        }
        
        // when
        describe(items[0])
    }

    fun maxOf(a: Int, b: Int): Int {
        return if (a > b) {
            a
        } else {
            b
        }
    }

    fun describe(obj: Any): String =
            when (obj) {
                1 -> "One"
                "Hello" -> "Greeting"
                is Long -> "Long"
                !is String -> "Not a string"
                else -> "Unknown"
            }

循環(huán)表達式和集合

    @Test
    fun testLoop() {
        val items = listOf("apple", "banana", "kiwifruit", "grape", "pear", "ab...")
        // for loop
        testForLoop(items)
        // collection
        testCollection(items)
    }

    private fun testForLoop(items: List<String>) {
        for (item in items) {
            println(item)
        }
        for (index in items.indices) {
            println("item at $index is ${items[index]}")
        }
        for (index in 0 until items.size) {
            println("item at $index is ${items[index]}")
        }
        for (index in items.size - 1 downTo 0 step 2) {
            println("item at $index is ${items[index]}")
        }
    }

    private fun testCollection(items: List<String>) {
        items.filter { it.startsWith("a") }
                .sortedBy { it }
                .map { it.toUpperCase() }
                .forEach { println(it) }
    }

類型檢測

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // `obj` 在該條件分支內(nèi)自動轉(zhuǎn)換成 `String`
        return obj.length
    }

    // 在離開類型檢測分支后,`obj` 仍然是 `Any` 類型
    return null
}

默認參數(shù)

    @Test
    fun testDefaultArgs() {
        println(createPerson("feifei1"))
        println(createPerson("feifei2", 12))
        println(createPerson("feifei3", 12, true))
    }

    private fun createPerson(name:String, age:Int = 0, isMale: Boolean = false):Person {
        return Person(name, age, isMale)
    }

數(shù)據(jù)模型

    // 會為 Customer 類提供以下功能:
    // 所有屬性的 getters (對于 var 定義的還有 setters)
    // equals()/hashCode()/toString()/copy()等
    data class Customer(val name: String, val email: String)

單例

    object Resource {
        val name = "Name"
    }

常量

class KotlinSyntax {
    companion object {
        const val PI = 3.14
    }
}

// 或者
   object Resource {
        val name = "Name"
        const val PI = 3.14
    }

方法作為其他方法的參數(shù)
一般用于回調(diào)

// 場景,有一個輪播條,點擊其中一個進入詳情
// 定義一個adapter,傳入值和對調(diào)函數(shù)
class BannerPagerAdapter (var srcs: List<String>,
                          private val callback: (position: Int)->Unit) : PagerAdapter() {
    override fun instantiateItem(container: ViewGroup, position: Int): Any {
        val view = LayoutInflater.from(container.context).inflate(R.layout.view_home_headline, container, false)
        val tvHeard = view.findViewById<TextView>(R.id.iv_headline)
        // 設置點擊監(jiān)聽
        tvHeard.setOnClickListener({callback(srcs[position])})
        container.addView(view)
        return view
    }
    // ...
}
    
    // 在Activity中設置
    private val bannerAdapter by lazy {
        BannerPagerAdapter(list_banner, callback = { title ->
            toBannerDetail(title)
        })
    }

    private fun toBannerDetail(title: String) {
        // to detail
    }

// or 簡單的例子
    @Test
    fun testHighOrderFunc() {
        val items = listOf("a", "b", "c")
        wrapDoSomething { println(items) }
    }

    private fun wrapDoSomething(doSomething:()->Unit) {
        println("-->")
        doSomething()
        println("<--")
    }

范圍外 run,let,with,apply

class Turtle {
        fun penDown(){}
        fun penUp(){}
        fun turn(degrees: Double){}
        fun forward(pixels: Double){}
    }

    // 對一個對象實例調(diào)用多個方法 (with)
    @Test
    fun testWith() {
        val myTurtle = Turtle()
        with(myTurtle) { // 畫一個 100 像素的正方形
            penDown()
            for(i in 1..4) {
                forward(100.0)
                turn(90.0)
            }
            penUp()
        }
    }

Java調(diào)用Kotlin

object KotlinObject {

    const val PI = 3.14

    fun max(a: Int, b: Int): Int {
        return if (a > b) {
            a
        } else {
            b
        }
    }
}

class KotlinClass {
    companion object {
        const val PI = 3.14
        fun max(a: Int, b: Int): Int {
            return if (a > b) {
                a
            } else {
                b
            }
        }
    }
}
    public void useKotlin() {
        // use object
        System.out.print(KotlinObject.PI);
        System.out.print(KotlinObject.INSTANCE.max(1,2));
        
        // use class static const or fun
        System.out.print(KotlinClass.PI);
        System.out.print(KotlinClass.Companion.max(1,2));
    }

用于Android開發(fā)的工具

  • Kotlin Android 擴展是一個編譯器擴展, 可以讓你擺脫代碼中的 findViewById() 調(diào)用,并將其替換為合成的編譯器生成的屬性。
  • Anko 是一個提供圍繞 Android API 的 Kotlin 友好的包裝器的庫 ,以及一個可以用 Kotlin 代碼替換布局 .xml 文件的 DSL。

如何在開發(fā)中使用Kotlin


非Android開發(fā)

請見官方文檔,或對應中文版

Android開發(fā)

創(chuàng)建新項目
Android Studio 3.0+完全支持kotlin,創(chuàng)建項目的時候,默認勾選支持Kotlin,創(chuàng)建完項目后會自動在gradle文件中引入插件和擴展插件。

Android Studio 3.0+創(chuàng)建新項目

創(chuàng)建完工程后,工程gradle文件中


工程gradle文件

應用Module gradle文件中


應用Module gradle文件

Java 轉(zhuǎn) Kotlin

  1. 切到要轉(zhuǎn)換的Java文件
  2. Android Studio菜單 Code -> ‘Convert Java File to Kotlin File’, 或 Mac環(huán)境下,雙擊Shite鍵,輸入 covert java....出現(xiàn)‘Convert Java File to Kotlin File’選項。

Java轉(zhuǎn)kotlin的常見問題
一般而言可以直接轉(zhuǎn)換,無需修改,但有時需要手動修改,一般在如下幾個地方出現(xiàn):

  1. 邏輯操作符。kotlin中只有Int類型的值才能直接進行邏輯運算,其他類型如Byte要轉(zhuǎn)為Int之后才能進行邏輯運算。且,kotlin中的邏輯運算是作為方法出現(xiàn)的。

    邏輯運算 Java vs Kotlin

  2. 單行賦值后進行比較
    這個在讀流操作時經(jīng)常出現(xiàn)

    private void readIS(InputStream is) throws Exception {
        int len = 0;
        byte[] bytes = new byte[1024];
        OutputStream os = new FileOutputStream("/file/text.txt");
        while ((len = is.read(bytes)) != -1) {
            os.write(bytes, 0, len);
        }
    }

直接轉(zhuǎn)換成kotlin是

   @Throws(Exception::class)
    private fun readIS(`is`: InputStream) { // is是kotlin關鍵字,所有做了處理
        var len = 0
        val bytes = ByteArray(1024)
        val os = FileOutputStream("/file/text.txt")
        while ((len = `is`.read(bytes)) != -1) {  // 這行報語法錯誤
            os.write(bytes, 0, len)
        }
    }

// 稍作修改,消除語法錯誤
    @Throws(Exception::class)
    private fun readIS(`is`: InputStream) {
        var len = 0
        val bytes = ByteArray(1024)
        val os = FileOutputStream("/file/text.txt")
        len = `is`.read(bytes)
        while (len != -1) {
            os.write(bytes, 0, len)
            len = `is`.read(bytes)
        }
    }

使用Kotlin常見問題有哪些?


待續(xù)

參考


Kotlin官網(wǎng)
Kotlin中文網(wǎng)
Android開發(fā)者官方
Android Kotlin Guides
Kotlin_Tips
Kotlin簡單編譯流程
Kotlin編譯過程分析

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

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

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