文章目录
- Kotlin泛型
- 声明处型变
- 协变< out T>
- 逆变< in T>
- 使用处型变(类型投影)
- 参考
Kotlin泛型
声明处型变
协变< out T>
interface GenericsP<T> {
fun get(): T //读取并返回T,可以认为只能读取T的对象是生产者
}
如上声明了GenericsP< T>接口,如果其内部只能读取并返回T,可以认为GenericsP实例对象为生产者(返回T)。继续声明如下实体类:
open class Book(val name: String)
data class EnglishBook(val english: String) : Book(english)
data class MathBook(val math: String) : Book(math)
已知EnglishBook、MathBook为Book的子类,通常可以像下面这样声明:
val book: Book = EnglishBook("英语")
一般在实现多态时会这么写,但是如果将Book、EnglishBook当成泛型放入GenericsP,他们之间的关系还成立吗?即:
可以看到编译器直接报错,因为虽然EnglishBook是Book的子类,但是GenericsP< EnglishBook>并不是GenericsP< Book>的子类,如果想让这个关系也成立,Kotlin提供了out修饰符,out修饰符能够确保:
1、T只能用于函数返回中,不能用于参数输入中;
2、GenericsP< EnglishBook>可以安全的作为GenericsP< Book>的子类
示例如下:
interface GenericsP<out T> {
fun get(): T //读取并返回T,可以认为只能读取T的对象是生产者
// fun put(item: T) //错误,不允许在输入参数中使用
}
经过如上的改动后,可以看到GenericsP< EnglishBook>可以正确赋值给GenericsP< Book>了:
逆变< in T>
interface GenericsC<T> {
fun put(item: T) //写入T,可以认为只能写入T的对象是消费者
}
如上声明了GenericsC< T>接口,如果其内部只能写入T,可以认为GenericsC实例对象为消费者(消费T)。为了保证T只能出现在参数输入位置,而不能出现在函数返回位置上,Kotlin可以使用in进行控制:
interface GenericsC<in T> {
fun put(item: T) //写入T,可以认为只能写入T的对象是消费者
//fun get(): T //错误,不允许在返回中使用
}
继续声明如下函数:
/**
* 称为GenericsC在Book上是逆变的。
* 跟系统源码中的Comparable类似
*/
private fun consume(to: GenericsC<Book>) {
//GenericsC<Book>实例赋值给了GenericsC<EnglishBook>
val target: GenericsC<EnglishBook> = to
target.put(EnglishBook("英语"))
}
可以看到GenericsC中的泛型参数声明为in后,GenericsC实例可以直接赋值给了GenericsC< EnglishBook>,称为GenericsC在Book上是逆变的。在系统源码中我们经常使用的一个例子就是Comparable:
//Comparable.kt
public interface Comparable<in T> {
public operator fun compareTo(other: T): Int
}
使用处型变(类型投影)
上一节中in、out都是写在类的声明处,从而控制泛型参数的使用场景,但是如果泛型参数既可能出现在函数入参中,又可能出现在函数返回中,典型的类就是Array:
class Array<T>(val size: Int) {
fun get(index: Int): T { …… }
fun set(index: Int, value: T) { …… }
}
这时候就不能在声明处做任何协变/逆变的操作了,如下函数中使用Array:
fun copy(from: Array<Any>, to: Array<Any>) {
if (from.size != to.size) return
for (i in from.indices)
to[i] = from[i]
}
调用方:
val strs: Array<String> = arrayOf("1", "2")
val any = Array<Any>(2) {}
copy(strs, any) //编译器报错 strs其类型为 Array<String> 但此处期望 Array<Any>
参考
【1】https://www.kotlincn.net/docs/reference/generics.html
【2】https://mp.weixin.qq.com/s/vSwx7fgROJcrQwEOW7Ws8A
【3】https://juejin.cn/post/7042606952311947278