kotlin的协变(out)与逆变(in)详解
关于协变与逆变的来源
Java中
List<String> list =new ArrayList()
List<Object> list2=list //编译失败
list2.add(new Date()) //list2可以增加进来是object对象
interface Collection<E>{
void addAll(Collection<E> items)
}
void copyAll(Collection<Object> to,Collection<String> from){
to.addAll(from) //编译失败,Collection<String>并不是Collection<Object>的子类
}
//正确做法是:
interface Collection<E>{
void addAll(Collection<? extends E> items)
}
void copyAll(Collection<Object> to,Collection<String> from){
to.addAll(from) //编译通过
}
//Collection<String> 就是Collection<? extends Object>的子类型。
//List<? super String> 这种就是逆变
我们如果只从中读取数据,而不往里面写入内容,那么这样的对象叫做生产者;如果只向里面写入数据,不读取数据就叫做消费者。
生产者使用 ? extends E; 消费者使用 ? super E。
PECS:Producer,Extends,Consumer,Super
Kotlin代码
Class MyClass<out t:T,in m:M>(t:T,m:M){
private var t:T
private var m:M
init{
this.t=t
this.m=m
}
fun get():T=this.t //out 只读取是协变 子类型对象赋给了父类型引用 ,只能用于输出类型。 producer ==output==out
fun set(m:M){
this.m=m // in 只写入是逆变,父类型对象赋给了子类型引用 ,只能用于输入类型。 customer ==intput==in
}
}
fun myTest(myClass:MyClass<String,Number>){
var myobject :MyClass<Any,Int>=myClass // String->Any 子类型对象赋给了父类型引用。Number->Int父类型对象赋给了子类型引用。
}
Kotlin:声明处协变;Java:使用处协变。Kotlin中的out关键字叫做variance annotation,因为它是在类型参数声明处所指定的,因此我们称之为声明处协变(declaration-site variance)。对于Java来说则是使用处协变(use-site variance),其中类型通配符使得类型协变成为可能。
//如果泛型类只是将泛型类作为其方法的输出类型,那么我就可以使用out。
interface Producer<out T>{
fun produce():T
}
//如果泛型类只是将泛型类作为其输入类型,我们就可以使用in。
interface Consunmer<in T>{
fun consume(item:T)
}
//如果泛型类中的方法同时有输入型与输出型,那么就不能用out与in来修饰泛型
interface ProducerConsume<T>{
fun produce():T
fun consume(item:T)
}
================举个实例====================
open class Fruit
open class Apple:Fruit()
open Orange:Apple()
class FruitProducer:Producer<Fruit> {
override fun produce():Fruit{
println("produce Fruit")
return Fruit()
}
}
class AppleProducer:Producer<Apple> {
override fun produce():Apple{
println("produce Apple")
return Apple()
}
}
class orangeProducer:Producer<Orange> {
override fun produce():Orange{
println("produce Orange")
return Orange()
}
}
fun main(args:Array<String>){
//对于out泛型来说,我们可以将子类型对象赋值给父类型引用
val producer1:Producer<Fruit>=FruitProducer()
val producer2:Producer<Fruit>=AppleProducer()
val producer3:Producer<Fruit>=OrangeProducer()
println("-----------------")
//对于in泛型来说,我们可以讲父类型对象赋给子类型引用
val consume1:Consumer<Orange>=Human()
val consume2:Consumer<Orange>=Man()
val consume3:Consumer<Orange>=Boy()
}
class Human:Consumer<Fruit>{
override fun consume(item:Fruit){
println("consume Fruit")
}
}
class Man:Consumer<Apple>{
override fun consume(item:Apple){
println("consume Apple")
}
}
class Boy:Consumer<Orange>{
override fun consume(item:Orange){
println("consume Orange")
}
}
使用处协变(use-site variance) 类型投影
数组的浅拷贝
fun copy(from:Array<out Any>,to:Array<Any>){
for(i in from.indices){
to[i]=from[i]
}
}
fun setValue(to:Array<in String>,index:Int,value:String){
to[index]=value
}
fun main(args: Array<String>) {
val from:Array<Int> = arrayOf(1,2,3,4)
val to:Array<Any> =Array<Any>(4,{"hello"+it})
for(item in to){
println(item)
}
copy(from,to)
println("-------------")
val array:Array<String> =Array<String>(4,{_->"hello"})
for(item in array){
println(item)
}
setValue(array,1,"world")
println("----------")
for (item in array){
println(item)
}
println("-------------")
val array2:Array<Any> =Array<Any>(4,{it->"hello"+it})
for(item in array2){
println(item)
}
setValue(array2,1,"world")
for(item in array2){
println(item)
}
}