[譯]Kotlin泛型中什麼時候該用類型形參約束?

翻譯說明:java

原標題: When (and when not) to Use Type Parameter Constraints in Kotlinapp

原文地址: typealias.com/guides/when…dom

原文做者: Dave Leedside

以前的Kotlin文章,歡迎查看:

翻譯系列:函數

原創系列:post

實戰系列:ui

簡述(又來皮一下):

今天這篇文章依舊很簡單,只要搞懂一個東西就能夠了。那就是泛型中的類型形參的約束,這個概念在Java中也有的。可是咱們有個疑惑是什麼狀況下使用泛型類型形參呢?spa

進入正題(開始表演...)

當你在聲明一個泛型時,Kotlin容許你給這個泛型的類型形參增長約束條件,換言之就是把類型形參可接受的類型實參限制在一個類型範圍內。那這個有什麼做用呢?讓咱們一塊兒來看下面這個例子:插件

想像下有這麼個需求場景: 假設你家裏有幾隻寵物,你想選擇一個最喜歡的:翻譯

fun <T> chooseFavorite(pets: List<T>): T {
    val favorite = pets[random.nextInt(pets.size)]
    // This next line won't compile - because `name` can't be resolved
    println("My favorite pet is ${favorite.name}")
    return favorite
}
複製代碼

println()函數聲明不會經過編譯,由於你沒法經過T來引用name屬性。由於T能夠是調用者指定的任何內容。例如,像如下代碼實現,T就是一個Int類型,很明顯它就沒有name屬性:

chooseFavorite(listOf(1, 2, 3))
複製代碼

解決方法一 - 放棄使用泛型

你能夠嘗試改造一下這個函數,把泛型去掉:

fun chooseFavorite(pets: List<Pet>): Pet {
    val favorite = pets[random.nextInt(pets.size)]
    println("My favorite pet is ${favorite.name}")
    return favorite
}
複製代碼

這個處理看起來貌似沒有什麼問題,可是咱們會遇到一種不想要的返回值類型。如下是咱們調用該函數時會發生的狀況:

val pets: List<Dog> = listOf(Dog("Rover"), Dog("Sheba"))
val favorite: Pet = chooseFavorite(pets)
複製代碼

儘管咱們聲明定義的是List< Dog >,可是經過chooseFavorite返回的是一個Pet類型,除非這裏咱們使用強制類型轉換。

解決辦法二 - 使用類型形參約束

咱們能夠經過指定上限來限制類型形參-換句話說就是指定你想要接收的超類型。在咱們的例子中,咱們但願此函數做爲List <Pet>處理List <Dog>List <Cat>也就是既包含Cat類型也包含Dog類型的混合Pet類型的列表中。

fun <T : Pet> chooseFavorite(pets: List<T>): T {
    val favorite = pets[random.nextInt(pets.size)]
    println("My favorite pet is ${favorite.name}")
    return favorite
}
複製代碼

這裏的類型形參的聲明是<T:Pet>,這個Pet就是上界約束。如今咱們已經指定了這個,調用代碼只能傳遞Pet類型以及它的子類型。

val pets: List<Dog> = listOf(Dog("Rover"), Dog("Sheba"))
val favorite: Dog = chooseFavorite(pets)
複製代碼

使用建議

下面兩種是你須要使用類型形參約束狀況:

  1. 當你在某個類型上調用特定的函數或屬性(即某個類型的類獨有的函數和屬性)
  2. 當你但願在函數返回時保留某個特定類型

這是一個快速的「備忘單」表,可幫助您決定哪一種狀況使用什麼?

須要調用成員(類的成員函數或屬性) 不須要調用成員(類的成員函數或屬性)
須要保留類型 使用帶有類型參數約束的泛型 使用不帶類型參數約束的泛型
不須要保留類型 使用非泛型和適當的抽象 使用Java中的原生態類型

如何指定約束

約束還有不少 - 您能夠指定多個參數的約束,以及對同一參數的多個約束。對於全部細節,請查看概念文章Type Parameter Constraint。您還能夠在Kotlin的官方參考文檔中快速查看經常使用約束。

讀者有話說

本篇文章核心點在於什麼狀況下該使用泛型約束,做者總結的很好就是那種表格,理解和掌握了那張表格,那麼你在使用泛型形參約束上就會成竹在胸。關於那個表格可能有點難理解,我這裏再補充解釋一下:

  • 首先,解釋下是否須要調用成員,意思是在定義函數聲明內部是否使用了泛型形參約束上界類型中對應類的特定成員包括函數或屬性,好比說chooseFavorite方法中的name屬性就是Pet這個類特定的成員屬性。總之一句話: 在函數定義內部是否須要調用特定類型的類的成員或屬性,這也就直接決定了是否須要帶類型參數約束,若是不須要調用成員,那麼就不須要帶類型參數約束
  • 而後,解釋下是否須要保留類型,就是在定義一個函數的時候,返回值的類型是否要保留泛型形參約束上界類型,主要做用就是避免強制類型轉換,例如例子中解決辦法一就是去除泛型,而是用了抽象的父類Pet,即便傳入的是List<Dog>,可是chooseFavorite方法返回依然是父類Pet,外部接收類型Dog因此避免不了強制類型轉換。總之一句話: 是否須要保留類型,也就直接決定了是否使用泛型,若是不保留類型的話能夠不使用泛型,函數外部接收者可使用抽象的父類來接收;若是保留類型,函數外部接收者就必須是明確一個類型,那麼此時若是還用抽象父類就避免不了類型轉換,那麼此時就應該使用泛型
  • 最後,得出簡單的總結:

一、是否須要調用成員決定了在使用泛型前提下,是否使用泛型類型參數約束;不然直接可使用抽象

二、是否保留類型決定了是否使用泛型

三、既保留類型又調用成員,則就是使用泛型且帶形參約束條件

四、不保留類型又調用成員,則就是不使用泛型和適當的抽象

五、保留類型不調用成員,則就是使用泛型不帶形參約束條件

六、不保留類型不調用成員,則就是使用java中原生態類型,例如Java中的List類型

歡迎關注Kotlin開發者聯盟,這裏有最新Kotlin技術文章,每週會不按期翻譯一篇Kotlin國外技術文章。若是你也喜歡Kotlin,歡迎加入咱們~~~

相關文章
相關標籤/搜索