Kotlin协变与逆变

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)
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值