Kotlin應用於專案踩過的坑

四級五次郎發表於2017-08-09

CSDN地址:blog.csdn.net/sinat_36668…

在谷歌宣佈Kotlin成為一級開發語言的時候就開始學習kotlin,現在已經在專案中開發使用了。我目前負責的專案老程式碼全是java,我不可能全轉成kotlin,所以即便使用了kotlin,也只是在新建檔案的程式碼裡使用,老程式碼繼續用java。kotlin的好處就是完全相容java,java呼叫kotlin,kotlin基本上無阻礙。官網的話就是java和kotlin 100%相容。

為什麼使用Kotlin

為什麼我要改用Kotlin,這是在一個Kotlin社群裡看到的,我覺得他說的比我說的好。Kotlin簡歷,語法簡單,空安全,效能突出,與Java互動性好什麼的。這寫都不是主要的,最重要的是大家都在學Kotlin,感覺不寫寫就要落伍了。思考再三,索性直接應用到專案中。

怎麼使用Kotlin

Kotlin在Android Studio下的初次使用 這篇是我之前自己寫的,我也是按照這個順序來整合的,說明一下,現在的最新版本是 v1.1.3-2。如果有需要大家可以自己查最新版本傳送門 。還沒有在Android Studio中整合Kotlin的可以先看這個。

專案中踩過的坑

1. Kotlin沒有配置直接使用

第一次建立Kotlin Activity,會提示 Kotlin not configured,我們直接點configure,如圖:

這裡寫圖片描述
這裡寫圖片描述

然後點 Android with Gradle

這裡寫圖片描述
這裡寫圖片描述

之後進入Kotlin配置介面,預設點 ok 即可

這裡寫圖片描述
這裡寫圖片描述

這樣也就配置完成了。這裡我沒有按照這個思路方法實現。我覺得這種方便是方便,但是會有種把自己性命交在其他人手上的感覺。配置好之後點選sync同步一下就OK了。
圖片來自blog.csdn.net/u010675012/…

2. 集合List不能addAll()

在下拉重新整理的時候,我將新得來的資料新增到之前的資料集合中,但是addAll()不讓用,最終查資料才知道是因為List集合中沒有這個方法,Are you kidding me???

原來在Kotlin中,明確的區分了可變和只讀的集合(list, set, map等),明確的確定了集合的可讀性,有助於良好的編碼,以及便於Bug的規避。

MutableList:

MutableList介面繼承於List,MutableCollection&ltE>,是對只讀集合的擴充套件,增加了了對集合的新增及刪除元素的操作。直接上程式碼

private var testList: List<Int>? = null
private fun testList(){
    ...
    testList.addAll(Int)  // 這裡addAll是爆紅,沒有這個方法的
    ...
}複製程式碼

修改:

private var testList: MutableList<Int>? = null
private fun testList(){
    ...
    testList.addAll(Int)  // 這樣就解決了這個問題
    ...
}複製程式碼

但是在交流過程中發現這樣也可是實現

 private var mList : ArrayList<String> = ArrayList()
    fun testAdd(){
        mList.addAll(mList1)  
        // 這裡也不報錯,這是Kotlin使用Java的ArrayList,互動良好由此可見。不建議這麼用
    }複製程式碼

還有一個是arrayListOf(),這個也能實現

    private var mList = arrayListOf<Int>()
    fun testget(position:Int){
        mList.add(1)
    }複製程式碼

這是為什麼呢?我們們一點一點的看,先看下MutableList的原始碼:

/**
 * A generic ordered collection of elements that supports adding and removing elements.
 * @param E the type of elements contained in the list. The mutable list is invariant on its element type.
 */
public interface MutableList<E> : List<E>, MutableCollection<E> {
    // Modification Operations
    override fun add(element: E): Boolean

    override fun remove(element: E): Boolean

    // Bulk Modification Operations
    override fun addAll(elements: Collection<E>): Boolean

    /**
     * Inserts all of the elements in the specified collection [elements] into this list at the specified [index].
     *
     * @return `true` if the list was changed as the result of the operation.
     */
    public fun addAll(index: Int, elements: Collection<E>): Boolean

    override fun removeAll(elements: Collection<E>): Boolean
    override fun retainAll(elements: Collection<E>): Boolean
    override fun clear(): Unit

    // Positional Access Operations
    /**
     * Replaces the element at the specified position in this list with the specified element.
     *
     * @return the element previously at the specified position.
     */
    public operator fun set(index: Int, element: E): E

    /**
     * Inserts an element into the list at the specified [index].
     */
    public fun add(index: Int, element: E): Unit

    /**
     * Removes an element at the specified [index] from the list.
     *
     * @return the element that has been removed.
     */
    public fun removeAt(index: Int): E

    // List Iterators
    override fun listIterator(): MutableListIterator<E>

    override fun listIterator(index: Int): MutableListIterator<E>

    // View
    override fun subList(fromIndex: Int, toIndex: Int): MutableList<E>
}複製程式碼

這裡MutableList繼承了List, MutableCollection,裡面重寫了很多的方法。這裡說的很詳細,我覺得有java基礎的都能看懂,這裡只提供一個思路---看原始碼。

再看下arrayListOf的原始碼:

/** Returns an empty new [ArrayList]. */
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> arrayListOf(): ArrayList<T> = ArrayList()複製程式碼

這裡使用 inline 行內函數Java中的ArrayList()函式,就是目的碼的增加為代價來換取時間的節省。行內函數又是什麼呢?為什麼會有行內函數呢?

呼叫某個函式實際上將程式執行順序轉移到該函式所存放在記憶體中某個地址,將函式的程式內容執行完後,再返回到轉去執行該函式前的地方。這種轉移操作要求在轉去前要保護現場並記憶執行的地址,轉回後先要恢復現場,並按原來儲存地址繼續執行。也就是通常說的壓棧和出棧。因此,函式呼叫要有一定的時間和空間方面的開銷。那麼對於那些函式體程式碼不是很大,又頻繁呼叫的函式來說,這個時間和空間的消耗會很大。

那怎麼解決這個效能消耗問題呢,這個時候需要引入行內函數了。行內函數就是在程式編譯時,編譯器將程式中出現的行內函數的呼叫表示式用行內函數的函式體來直接進行替換。顯然,這樣就不會產生轉去轉回的問題,但是由於在編譯時將函式體中的程式碼被替代到程式中,因此會增加目標程式程式碼量,進而增加空間開銷,而在時間代銷上不象函式呼叫時那麼大,可見它是以目的碼的增加為代價來換取時間的節省。

listOf

listOf()是使用ArrayList實現的,返回的list是隻讀的,其記憶體效率更高。在開發過程中,可以儘可能的多用只讀List,可以在一定程度上提高記憶體效率。

    private var mList = listOf<Int>()
    fun testget(position:Int){
        mList.get(position)
    }複製程式碼

已經成為原始碼狂魔的你肯定還想再看listOf()的原始碼:

/** Returns an empty read-only list.  The returned list is serializable (JVM). */
@kotlin.internal.InlineOnly
public inline fun <T> listOf(): List<T> = emptyList()複製程式碼

看註釋很明白,這裡返回一個只讀的空集合且實現了序列化。我們們接著看emptyList()的原始碼:

/** Returns an empty read-only list.  The returned list is serializable (JVM). */
public fun <T> emptyList(): List<T> = EmptyList複製程式碼

這就已經浮出水面了,emptyList()是List型別。我們們接著看EmptyList的原始碼:


internal object EmptyList : List<Nothing>, Serializable, RandomAccess {
    private const val serialVersionUID: Long = -7390468764508069838L

    override fun equals(other: Any?): Boolean = other is List<*> && other.isEmpty()
    override fun hashCode(): Int = 1
    override fun toString(): String = "[]"

    override val size: Int get() = 0
    override fun isEmpty(): Boolean = true
    override fun contains(element: Nothing): Boolean = false
    override fun containsAll(elements: Collection<Nothing>): Boolean = elements.isEmpty()

    override fun get(index: Int): Nothing = throw IndexOutOfBoundsException("Empty list doesn't contain element at index $index.")
    override fun indexOf(element: Nothing): Int = -1
    override fun lastIndexOf(element: Nothing): Int = -1

    override fun iterator(): Iterator<Nothing> = EmptyIterator
    override fun listIterator(): ListIterator<Nothing> = EmptyIterator
    override fun listIterator(index: Int): ListIterator<Nothing> {
        if (index != 0) throw IndexOutOfBoundsException("Index: $index")
        return EmptyIterator
    }

    override fun subList(fromIndex: Int, toIndex: Int): List<Nothing> {
        if (fromIndex == 0 && toIndex == 0) return this
        throw IndexOutOfBoundsException("fromIndex: $fromIndex, toIndex: $toIndex")
    }

    private fun readResolve(): Any = EmptyList
}複製程式碼

到這裡我們就看出來了ListOf()中所有的方法。

3. 運算子號報錯

這裡寫圖片描述
這裡寫圖片描述

這裡 - 報錯,這怎麼會出錯?對於剛用於專案中的我也是一臉懵逼。原來使Kotlin的非空機制導致的。因為你的mList?.size加了一個問號所以有可能返回null

    private var mList: MutableList<String>? = null
    fun testget(): Int {
        mList?.add("這是第一個:" + 1)
        mList?.add("這是第二個:" + 2)
        return mList?.size - 1   // 報錯
    }複製程式碼

怎麼處理這個呢,很簡單,直接加上!!,注意是兩個

    private var mList: MutableList<String>? = null
    fun testget(): Int {
        mList?.add("這是第一個:" + 1)
        mList?.add("這是第二個:" + 2)
        return mList?.size!! - 1   // 通過
    }複製程式碼

為什麼呢?!!操作符,丟擲一個非空的B 或者空KNPE(KotlinNullPointerException)。

這裡會丟擲KotlinNullPointerException這個異常。因為我們們的mList是null。有人說這麼多操作符看著彆扭,怎麼才能不那麼多的操作符呢?

// 從上面知道,這種方式簡單粗暴
    private var mList = arrayListOf<String>()
    fun testget(): Int {
        mList.add("這是第一個:" + 1)
        mList.add("這是第二個:" + 2)
        return mList.size - 1
    }複製程式碼

其他的還需要小夥伴們自己發掘。

由於篇幅關係只能先到這了,有錯誤的地方歡迎留言指正。

CSDN地址:blog.csdn.net/sinat_36668…
掘金主頁:juejin.im/user/582991…

Kotlin社群交流群:302755325

這裡寫圖片描述
這裡寫圖片描述

相關文章