iT邦幫忙

2021 iThome 鐵人賽

DAY 19
0
Software Development

溫柔學姐的Kotlin補課/教學系列 第 19

想要彈性類別嗎,讓類別當參數吧:泛型 Generics

  • 分享至 

  • xImage
  •  

寬廣的室外網球場上,學生們正在做發球考試的練習。

「嘿!」女孩左手將球向上輕拋,右手握拍奮力用全身的力量擊球。

和羽球重視甩腕的方式不同,網球需要軀幹旋轉的力量,否則很容易受傷。

球網對面的練習搭擋迅速撿球發回,這次換成女孩緊盯著球的飛行拋物線。

「幸好發球規則有指定落點要在對角發球區,雙人練習就不用來回奔波。」詩憶鬆了口氣,彎腰把還在滾動的球撿起來。「嗯?這麼說起來,函式參數不是一開始就要指定類別嗎?那Collectionsadd函式是怎麼設計成可以根據成員類別彈性變化?」

她一邊思考一邊發球,一不留神就把球打出了界外,最後收到了搭擋的憤怒警告,她只好先甩開腦裡的困惑,畢竟球場上到處都是飛球,很危險的。

很快的,又到了晚上的補課時間。

唯心聽完詩憶的問題,很高興她開始深入思考程式的架構。

「妳察覺到今天的主題,泛型的存在了。」唯心用食指指節輕輕敲擊白板剛寫上的Generics。「泛型,就是將類別也當作一種參數宣告,宣告時需要放在大小於符號中間,比如List就是宣告成List<T>,要指定T的時候則用List<String>List<Drink>等的寫法。」

    //可以變動的飲料清單
    val drinks: MutableList<Drink>

為了更詳盡的說明,她打開標準函式庫裡的List的程式碼檔案給詩憶參考。「對了,泛型參數名字不一定要用TypeT,像有些文件寫的是List<E>E來自Elements。另一個常用的名字是ResultR。」

public interface List<out E> : Collection<E> {
    /**
     * Returns the element at the specified index in the list.
     */
    public operator fun get(index: Int): E

    /**
     * Returns a view of the portion of this list between the specified [fromIndex] (inclusive) and [toIndex] (exclusive).
     * The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
     *
     * Structural changes in the base list make the behavior of the view undefined.
     */
    public fun subList(fromIndex: Int, toIndex: Int): List<E>
}

「妳看,因為宣告了List<out E>,所以取得裡面的元素就可以宣告成public operator fun get(index: Int): E。」唯心指了指其中最簡單的函式。

「學姐,可是我看程式碼裡不是List<E>而是List<out E>?」詩憶馬上發現了問題。

「喔,List<E>就是List<out E>List<in E>的聯集,out的意思是限定函式只能把該泛型放在回傳的位置,in是限定函式把該泛型放在參數的位置。所以可以變動成員的MutableList<E>就還需要支援in的部分。」唯心將List.kt的程式碼向下滾動到MutableList的部分。

public interface MutableList<E> : List<E>, MutableCollection<E> {    /**
     * Adds the specified element to the end of this list.
     *
     * @return `true` because the list is always modified as the result of this operation.
     */
    override fun add(element: E): Boolean
    
    override fun remove(element: E): Boolean
}

唯心瞥了一眼手上的講義,補充說。「而如果要支援多個泛型,和函式參數一樣,用逗號分隔。此外,泛型也可以限定範圍,比如說希望都是繼承某個類別的類型或介面。」

public interface Sample<T : 某個可繼承的類型或介面, R> {
}

詩憶往前翻了翻繼承和介面相關的筆記。「之前說類別可以擴展多個介面,那泛型也可以限定多個介面嗎?」

「可以,不過語法比較複雜,課堂上沒特別提,我也沒怎麼用過,我找找官網文件唷。」唯心很快找到了說明泛型的頁面。「要用where描述,這裡有個範例可以參考。」

https://kotlinlang.org/docs/generics.html#upper-bounds

fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
    where T : CharSequence,
          T : Comparable<T> {
    return list.filter { it > threshold }.map { it.toString() }
}

上一篇
學校的白色咖啡屋(二):效率的勝利 Collections And Sequences
下一篇
在程式裡避開踩雷:安全引用空虛值、例外處理和延後、惰性初始化 Null Safety, Exception, lateinit, lazy
系列文
溫柔學姐的Kotlin補課/教學31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言