Kotlin泛型 協變及逆變

首先看看在Java中的泛型

List<Object> list1;

這樣聲明,表示我可以給list1放置任何類型的對象。

List<String> list2;

這樣表明,list2只可以放置類型是String的對象。

但是在java中下面這樣情況是不允許的

List<String> list1 = new ArrayList();

List<Object> list2 = list1 ; //編譯失敗

這意味着 List<String> 並不是 List<Object> 的子類型

爲什麼呢?

原因是:假如說是允許的

List<String> list1 = new ArrayList();

List<Object> list2 = list1 ; //編譯失敗

list2.add(0);

String str = list2.get(0);//ClassCastException:無法將整數轉換爲字符串

Int類型轉成字符串類型String,肯定是會類型轉換異常的。

Java是不允許我們這樣做的,那麼爲了解決這個問題,Java提供了通配符。

List<? extends Object> list;

表示我往List裏面可以放置Object以及Object任意的子類型。

List<Object> list;

這個表示我只能放置Object類型。

他們是非常類似的,Java中所以的類型都是Object類型的。

接下里再看一個例子:實現把另外一個集合添加到當前的集合裏面,

interface Collection<E> …… {
  void addAll(Collection<E> items);
}

一般情況下,Collection<E>的泛型是E類型的,那麼addAll()裏面添加進來的集合也應該是Collection<E>,這樣是可以完美的添加進去。

那麼看看這個方法能不能編譯通過?

void copyAll(Collection<Object> to, Collection<String> from) {
  to.addAll(from);
}

 對於這種簡單聲明的 addAll 將不能編譯:因爲 Collection<String> 不是 Collection<Object> 的子類型,這樣寫是錯誤的。

實際Java中的addAll的聲明是這樣的

interface Collection<E> …… {
  void addAll(Collection<? extends E> items);
}

通配符類型參數 ? extends E 表示此方法接受 E 或者 E 的 一些子類型對象的集合,而不只是 E 自身。

可以看出 Collection<String>就是 Collection<? extends Object>的子類型

但是 Collection<String>絕對不是 Collection<Object>的子類型

這種表示形式限定了上界,這種情況稱之爲協變

接下里看下與之對應的逆變

List<? super String>

這裏你只能調用接受 String 作爲參數以及String層次體系上面的類型。

我們如果只從中讀取數據,而不往裏面寫入內容,那麼這樣的對象叫生產者;如果只向裏面寫入數據,而不從中讀取數據,那麼這樣的對象叫作消費者。

生產者使用?extends E  ;消費者使用?super  E

言歸正傳看看Kotlin如何解決協變和逆變相關的問題的

class MyClass<T>(t: T) {
    private var t: T
    init {
        this.t = t
    }
    fun get(): T = this.t
} 

fun myTest(myClass: MyClass<String>) {
    var myObject: MyClass<String> = myClass
    println(myObject.get())
 }
  
override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     setContentView(R.layout.activity_main)

     var myClass = MyClass<String>("abc")
     myTest(myClass)
 }

Kotlin 中所有類型的父類是Any ,我對上面的程序做點改變

這樣是不允許的,加個關鍵字out,就能正常執行

class MyClass<out T>(t: T) {
    private var t: T
    init {
        this.t = t
    }
    fun get(): T = this.t
}

這是爲什麼呢?

out表示只會讀取,並不會修改這個值,就是協變

還有in

class MyClass<out T, 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
    
    fun set(m:M){
        this.m = m
    }
}

 fun myTest(myClass: MyClass<String,Int>) {
     var myObject: MyClass<Any,Int> = myClass
     println(myObject.get())
 }

 override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     setContentView(R.layout.activity_main)

     var myClass = MyClass<String,Int>("abc",20)
     myTest(myClass)
}

這樣也可以正常運行

修改下程序 ,Int的父類是Number

加上in 可以解決問題

class MyClass<out T, in 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
    fun set(m:M){
        this.m = m
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章