首先看看在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
}
}