0%

Kotlin 泛型

reified解决的是什么问题?

泛型会在编译后类型擦除,在运行时无法获得泛型类型T的类型信息。

想要在运行时获取泛型类型信息,需要使用reified和inline配合。

Kotlin编译器会将reified方法内联(inline)到调用的地方(call-site)。

方法被内联到调用的地方后,泛型T会被替换成具体的类型。

所以 reified 使得泛型的方法假装在运行时能够获取泛型的类信息。

这样不用为了获取泛型的类型再单独给方法传一个Class参数。

协变是什么意思?

带 extends 限定(上界)的通配符类型使得类型是协变的(covariant)。

逆变是什么意思?

super限定的下界通配符使得类型是逆变的(contravariance)。

在 Java 中有 List<? super String>List<Object>List<String> 的一个超类。

in和out关键字是做什么的?

使用关键字 out 来支持协变,等同于 Java 中的上界通配符 ? extends。

使用关键字 in 来支持逆变,等同于 Java 中的下界通配符 ? super。

上界通配符用于读取,是产出东西给外面用,所以是out。

下界通配符用于修改,是生产东西存到容器里,所以是in。

消费者 in, 生产者 out。

kotlin泛型中的where有什么作用?

Java 中声明类或接口的时候,可以使用 extends 来设置边界,将泛型类型参数限制为某个类型的子集:

1
2
3
// T的类型必须是 Animal 的子类型
class Monster<T extends Animal>{
}

注意这个和前面讲的声明变量时的泛型类型声明是不同的东西,这里并没有 ?。
同时这个边界是可以设置多个,用 & 符号连接:

1
2
3
// T 的类型必须同时是 Animal 和 Food 的子类型
class Monster<T extends Animal & Food>{
}

Kotlin 只是把 extends 换成了 : 冒号。
class Monster<T : Animal>
设置多个边界可以使用 where 关键字:
class Monster<T> where T : Animal, T : Food
有人在看文档的时候觉得这个 where 是个新东西,其实虽然 Java 里没有 where ,但它并没有带来新功能,只是把一个老功能换了个新写法。

不过笔者觉得 Kotlin 里 where 这样的写法可读性更符合英文里的语法,尤其是如果 Monster 本身还有继承的时候:

1
2
class Monster<T> : MonsterParent<T>
where T : Animal

实现一个 fill 函数,传入一个 Array 和一个对象,将对象填充到 Array 中,要求 Array 参数的泛型支持逆变(假设 Array size 为 1)

1
2
3
fun <T> fill(to: Array<in T>, from: T) {
to[0] = from
}

实现一个 copy 函数,传入两个 Array 参数,将一个 Array 中的元素复制到另外个 Array 中,要求 Array 参数的泛型分别支持协变和逆变。

1
2
3
4
5
6
fun <T> copy(from: Array<out T>, to: Array<in T>) {
assert(from.size == to.size)
for (i in from.indices) {
to[i] = from[i]
}
}