最近在看《Kotlin核心编程》这本书,关于高阶类型这一章节晦涩难懂,当然可能与自己的知识水平有关,在参阅其他资料后才大概明白作者的意思,遂作此文,把书本的案例重新编排下。
1、问题
假设我们给集合类型定义了Iterable2<T>的接口,目的为了遍历集合:
interface Iterable2<T> {
fun iterator(): Iterable2<T>
}
然后让不同的集合实现这个接口,比如列表这样定义:
interface List2<T>: Iterable2<T> {
override fun iterator(): List2<T>
}
Set中这样定义:
interface Set2<T>: Iterable2<T> {
override fun iterator(): Set2<T>
}
同样一个接口,就因为返回的集合类型不同,就要在每种集合中都这样定义,100种集合定义100次,对程序员来说是不可接受的!
那有同学说了,可否在Iterable2中加一个类型R,来表示返回的类型,如下:
interface Iterable2<T, R> {
fun iterator(): R
}
然后我们迅速发现,下面问号不知道该填啥。
interface List2<T>: Iterable2<T, ?> {
}
因为kotlin中没法把List、Set这些类型本身作为泛型的类型参数。
2、设想
我们换个思路,如果有个函数,可以生成List、Set这些集合类型,然后把生成的类型作为上面的R,是不是问题就解决了?
我们姑且把这个函数称为F(X),其中X是集合中的参数类型,可以简单地理解为函数中的自变量,而F(X)就表示某一集合的类型,如F_LIST(T)就是List<T>这个类型本身,F_SET(T)就是Sett<T>这个类型本身。
关于这个“类型本身”,大家可以细品下。
然后,我们改写下Iterable2<T>如下:
interface Iterable2<T, F<X>> {
fun iterator(): F<T> // 令X = T
}
那么List、Set不用再写重写iterator()方法,可以这样写:
/**
* List#iterator定义
*/
interface List2<T>: Iterable2<F_LIST(T)>
/**
* Set#iterator定义
*/
interface Set2<T>: Iterable2<F_SET(T)>
3、方案
然而……,kotlin中不存在这样的F(X)。
既然没有,我们就创造一个类型,其中F和X都作为变数。
interface Kind<F, X>
就用这个类型来表示F传入X后生成的新类型。
我们再来改写下kotlin版本的Iterable2:
interface Iterable2<F, T> {
fun Kind<F, T>.iterator(): Kind<F, T>
}
其中T,表示集合的元素类型,iterator()返回的Kind<F, T>表示F传入T后生成的新类型。
细心的朋友应该注意到了,iterator()是Kind<F, T>的扩展方法。
如果所有的集合都继承Kind<F, T>,也就是:
interface List2<T>: Kind<List2.ToListType, T> {
/**
* 就是上面提到的F,它就是一个标识,不要去钻牛角尖地考虑怎么实现
*/
object ToListType
}
那么根据扩展方法的规则,所有集合都有iterator()方法。
那么这样绕来绕去到底有什么用呢?
这时候你会发现,完全不用像最开始那样分别在List、Set中写iterator()了。
用的时候直接用!!!
val listImpl = object : Iterable2<List2.ToListType, String> {
override fun Kind<List2.ToListType, String>.iterator(): Kind<List2.ToListType, String> {
TODO("Not yet implemented")
}
}
根据我们上面定义的Kind<List2.ToListType,String>含义,这个就表示List<String>,尽管看起来仍然很抽象。
其他的集合类型可以同样这样搞大家可以亲自动手试下噢。
剩下的内容大家可以去看《Kotlin核心编程》,这里就不再赘述了。