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)
}
}