in和out
和java一樣,kotlin中也有泛型的概念。不同的是苍蔬,java中使用了通配符而kotlin中不存在。本文主要介紹兩者的不同之處蝴蜓。
java中為了解決
List<String> strs = new ArrayList<String>();
List<Object> objs = strs;
類似語句中strs復制objs報錯的問題碟绑,使用了通配符,如常見的<? extends Object>
,<? super Object>
茎匠;在kotlin中則要在使用泛型出out
和in
來限定泛型是否允許插入和讀取格仲。in
修飾的泛型只能寫入,out
修飾的泛型只能讀取诵冒。
舉個例子:
我們定義一個copy方法凯肋,將第一個數(shù)組的內(nèi)容拷貝到第二個數(shù)組中去
val ints: Array<Int> = arrayOf(1, 2, 3)
val any = Array<Any>(3)
copy(ints, any)
錯誤示例一:
fun copy(from: Array<Any>, to: Array<Any>) {
assert(from.size == to.size)
for (i in from.indices)
to[i] = from[i]
}
這里調(diào)用copy(ints, any) // 錯誤:期望 (Array<Any>, Array<Any>),
因為Array<Int>不是Array<Any>的子類汽馋,編譯器認為我們可能修改里面的信息侮东,向其中加入非Any類型的對象,所以報錯
正確示例一
fun copy(from: Array<out Any>, to: Array<Any>) {
// ……
}
這個函數(shù)和上面那個唯一的不同就是參數(shù)from中增加了out關鍵字豹芯,它等同于java中的<? extends object>
表明from這個對象是能用來做source悄雅,并讀取里面的信息,不能向內(nèi)增加數(shù)據(jù)
同樣的铁蹈,我們也可以為第二個參數(shù)增加修飾的in宽闲,等同于java中的<? super Object>
,to只能接收Any以及其父類(ps:在本例中木缝,雖然可以通過編譯器便锨,但是這樣寫已經(jīng)無意義)
正確示例二
fun copy(from: Array<out Any>, to: Array<in Any>) {
// ……
}
根據(jù)官網(wǎng)的介紹,我們可以把from稱作生產(chǎn)者我碟,to成為消費者放案。
星投影
看到這里我們已經(jīng)學到了它們的基本用法,下面來將兩個特殊的星投影
- 對于
Foo <out T>
矫俺,其中T
是一個具有上界TUpper
的協(xié)變類型參數(shù)吱殉,Foo <*>
等價于Foo <out TUpper>
。 這意味著當T
未知時厘托,你可以安全地從Foo <*>
讀取TUpper
的值友雳。 - 對于
Foo <in T>
,其中T
是一個逆變類型參數(shù)铅匹,Foo <*>
等價于Foo <in Nothing>
押赊。 這意味著當 T 未知時,沒有什么可以以安全的方式寫入Foo <*>
包斑。 - 對于
Foo <T>
流礁,其中T
是一個具有上界TUpper
的不型變類型參數(shù)涕俗,Foo<*>
對于讀取值時等價于Foo<out TUpper>
而對于寫值時等價于Foo<in Nothing>
。
如果泛型類型具有多個類型參數(shù)神帅,則每個類型參數(shù)都可以單獨投影再姑。 例如,如果類型被聲明為interface Function <in T, out U>
找御,我們可以想象以下星投影:
Function<*, String> 表示 Function<in Nothing, String>元镀;
Function<Int, *> 表示 Function<Int, out Any?>;
Function<*, *> 表示 Function<in Nothing, out Any?>霎桅。
注意:星投影非常像 Java 的原始類型盾饮,但是安全及舍。
(以下內(nèi)容官網(wǎng)已經(jīng)將的很詳細义起,這里直接照抄)
泛型函數(shù)
不僅類可以有類型參數(shù)淆两。函數(shù)也可以有住闯。類型參數(shù)要放在函數(shù)名稱之前:
fun <T> singletonList(item: T): List<T> {
// ……
}
fun <T> T.basicToString() : String { // 擴展函數(shù)
// ……
}
要調(diào)用泛型函數(shù)瓜浸,在調(diào)用處函數(shù)名之后指定類型參數(shù)即可:
val l = singletonList<Int>(1)
泛型約束
能夠替換給定類型參數(shù)的所有可能類型的集合可以由泛型約束限制。
上界
最常見的約束類型是與 Java 的 extends
關鍵字對應的 上界:
fun <T : Comparable<T>> sort(list: List<T>) {
// ……
}
冒號之后指定的類型是上界:只有 Comparable<T>
的子類型可以替代T
比原。 例如
sort(listOf(1, 2, 3)) // OK插佛。Int 是 Comparable<Int> 的子類型
sort(listOf(HashMap<Int, String>())) // 錯誤:HashMap<Int,String> 不是 Comparable<HashMap<Int, String>> 的子類型
默認的上界(如果沒有聲明)是 Any?
。在尖括號中只能指定一個上界量窘。 如果同一類型參數(shù)需要多個上界雇寇,我們需要一個單獨的 where-子句:
fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<T>
where T : Comparable,
T : Cloneable {
return list.filter { it > threshold }.map { it.clone() }
}