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)
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章