想要弹性类别吗,让类别当参数吧:泛型 Generics

宽广的室外网球场上,学生们正在做发球考试的练习。

「嘿!」女孩左手将球向上轻抛,右手握拍奋力用全身的力量击球。

和羽球重视甩腕的方式不同,网球需要躯干旋转的力量,否则很容易受伤。

球网对面的练习搭挡迅速捡球发回,这次换成女孩紧盯着球的飞行抛物线。

「幸好发球规则有指定落点要在对角发球区,双人练习就不用来回奔波。」诗忆松了口气,弯腰把还在滚动的球捡起来。「嗯?这麽说起来,函式参数不是一开始就要指定类别吗?那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() }
}

<<:  Day10 - 子元件透过 emit event 触发父元件事件

>>:  连续 30 天 玩玩看 ProtoPie - Day 9

Day 24 : Jenkins 在Build完通知与好用套件

介绍Jenkins的章节即将进入尾声了。事实上你可能会想Jenkins默认介面这麽老气,怎麽就成为全...

Day 26 测试 React 元件:使用 React Testing Library 体验 Test Driven Development (TDD) - 6

前面几天我们已经用 TDD 的方式完成了 <Editor /> 元件,但不要忘了 TDD...

Day 07: Python基础必备小知识(下)

Python基础语法 列印讯息 print(“所输入的讯息”, 变数名称) 这是一个会将输入的讯息所...

Android Studio初学笔记-Day24-FloatButton和SnackBar

FloatButton和Snackbar FloatButton是漂浮式按钮,也可以说是不同种设计的...

[Day 4] 咱们一起做资料清理和前处理

咱们一起做资料清理和前处理 今日学习目标 资料如何清理 什麽是资料清理? 资料前处理的方式 为什麽资...