Kotlin Vocabulary | 操作符重載

當我們在處理可以添加、刪除、比較或者連接的類型時,我們通常需要寫很多冗長和重複的代碼。但在 Kotlin 中,我們可以藉助操作符重載,爲這些類型寫出更具表現力和簡潔的代碼。

  • 操作符重載

    https://kotlinlang.org/docs/reference/operator-overloading.html

我除了喜歡 Android,還喜歡在合唱團裏唱歌,所以就讓我們用合唱團的例子來說明操作符重載的好處。假設有一個由歌手組成的合唱團,我們想在合唱團中增加一名歌手,代碼如下:

<!-- Copyright 2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->


class Choir {
    private val singers = mutableListOf<Singer>()


    fun addSinger(singer: Singer) {
        singers.add(singer)
    }


    ...
}

我們將添加一名新歌手,示例如下:

choir.addSinger(singer)

但是比起這種操作,使用 += 來操作會更適合一些,而且這樣調用也更自然。

choir += singer

接着往下讀,您會知道:

  • 如何在 Kotlin 中通過操作符重載實現這一點;

  • 什麼樣的操作符可以被實現以及在 Android 的哪些場景下使用操作符會更有優勢;

  • 在實現操作符重載時要注意的最佳實踐方法;

  • Kotlin 編譯器如何實現操作符重載。

操作符重載的基礎

通過操作符重載,可以實現任意類型的一系列預定義操作符。操作符可以通過成員函數或者使用相應的成員函數的擴展函數來重載。比如: + 操作符可以通過 plus() 函數進行重載,+= 操作符可以通過 plusAssign() 函數進行重載。注意,操作符之間不會相互影響: 如果您重載了 +,是不會影響到 ++。

要重載一個操作符,您需要在 fun 的前面添加 operator 關鍵字,然後指定您想重載的操作符。如果您不添加 operator 關鍵字,編譯器會把它當作一個普通的 Kotlin 函數來處理,甚至不會進行編譯!

以下是 Kotlin 中可以重載的操作符:

△ 有關可以重載的操作符及其相應函數的完整列表,

請參見相關文檔

  • 相關文檔

    https://kotlinlang.org/docs/reference/operator-overloading.html

怎麼做

好了,開始吧,我們怎麼才能在 Kotlin 中實現操作符的重載?

讓我們使用初始示例中的 choir 類,我們需要重載 += 操作符來添加一名歌手。

<!-- Copyright 2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->


class Choir {
    private val singers = mutableListOf<Singer>()


    operator fun plusAssign(singer: Singer) {        
        singers.add(singer)
    }
}

您可以這樣使用操作符:

<!-- Copyright 2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->


data class Singer(val name: String)


fun main() {
    val choir = Choir()
    val singerMeghan = Singer("Meghan")
    choir += singerMeghan
}

重載的操作符可以使代碼更加的簡潔和易讀。

您希望重載其他哪些操作符?

通常情況下您需要的操作符不止一個,但是重載一個自定義類型的所有操作符可能是沒有任何意義的。過度的使用操作符重載會導致代碼的可讀性變差。所以需要多花點時間思考,對哪些操作符進行重載,可以提升代碼的可讀性。

我們重載 += 操作符是爲了將某人加入合唱團,但我們可能也想看看這個人是否已經是合唱團的成員。要實現這一點,我們需要重載 contains 函數,這樣我們就可以使用 in 操作符。

<!-- Copyright 2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->


operator fun contains(s: Singer) : Boolean {
       return singers.contains(s)
}
<!-- Copyright 2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->


data class Singer(val name: String) 
fun main() {
    val choir = Choir()
    val singerMeghan = Singer("Meghan")
    choir += singerMeghan
    if(singerMeghan in choir){
        println("Meghan is a part of the choir!")
    }
}

擴展中的操作符重載

也可以通過擴展函數來使用操作符重載。在這個示例中,我們重載了 ViewGroup 的 += 操作符:

operator fun ViewGroup.plusAssign(other: View) = addView(other)

現在給 viewGroup 添加一個 view 是如此的簡單!

viewGroup += view

來自其他語言的最佳實踐

操作符重載也在許多其他編程語言中使用,比如: C++、Python、Swift 和 PHP。雖然我們在 Kotlin 中暫時還沒有明確的最佳實踐,但我們可以從這些語言中學習一些:

  • 簡潔性並不總是意味着更易讀的代碼。想一下,如果您的代碼中加入了操作符重載,那麼您的代碼是不是真的會更加易讀;

  • 如果重載的結果在語言的上下文中沒有什麼意義,或者有任何不清晰的地方,您應該考慮使用函數來代替。比如,如果您添加了兩本書,那麼最終的結果會是什麼,並不是馬上就能看清楚的。會是一本新書嗎?它們會如何組合?如果有疑問,那麼您應該用函數來代替;

  • 如果一個操作符被重載了,那麼應該考慮一下與之對應的其他操作符也要被重載。比如,如果重載了 -,-= 也要考慮被重載。在我們的合唱團例子中,由於我們可以用 += 添加一名歌手,那麼我們也應該可以用 -= 刪除一名歌手。

這是怎麼實現的?

操作符重載是通過重寫操作符的標準函數調用實現的,比如,添加合唱團成員的代碼:

<!-- Copyright 2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->


val choir = Choir()
val singerMeghan = Singer("Meghan")
choir += singerMeghan

如果我們看一下反編譯的 Java 代碼,可以看到它是如何工作的:

<!-- Copyright 2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->


Choir choir = new Choir();
Singer singerMeghan = new Singer("Meghan");
choir.plusAssign(singerMeghan);

編譯器只是簡單的通過正常的成員函數調用替換了 +=。

總結

操作符重載必須謹慎使用,但是如果您使用得當,它是一個可以使代碼更具表現力和更加簡潔的強大工具。

  • 確保您使用了operator 關鍵字,否則 Kotlin 會將函數視爲一個普通函數來對待,並且代碼也將編譯失敗;

  • 檢查操作符重載是否使代碼更加易讀;

  • 仔細思考哪些操作符的重載對類型來說更有意義。


推薦閱讀




 點擊屏末  | 瞭解更多關於用 Kotlin 進行 Android 開發的相關資料


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章