文章目录
- 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



















