官方文檔: http://kotlinlang.org/docs/reference/generics.html
1.泛型(generics)
與Java類似仿贬,Kotlin的類也有類型參數(shù)(泛型):
class Box<T>(t: T) {
var value = t
}
一般情況,使用泛型實例,需要類型參數(shù):
val box: Box<Int> = Box<Int>(1)
如果類型參數(shù)可推斷出來,可省略類型參數(shù):
val box = Box(1) // 1是Int,編譯器可推斷出Box<Int>
2.型變(Variance)
Java泛型中最棘手部分就是通配符類型(初學(xué)實在頭暈),但kotlin沒有!
所以Kotlin通過型變(Variance)彌補:
聲明處型變(declaration-site variance)
類型投影(type projections)
為什么Java泛型需要通配符類型超歌?
在Effective Java解釋了該問題—第28條:利用有限制通配符來提升API的靈活性挟秤。
Java泛型是不型變的,意味著List<String>不是List<Object>子類型!
如果List是型變的,如下代碼編譯正常,但運行時出現(xiàn)異常:
// Java
List<String> strs = new ArrayList<String>();
List<Object> objs = strs; //錯誤,Java禁止型變!
objs.add(1);
String s = strs.get(0); //運行出現(xiàn)異常ClassCastException:無法將整數(shù)轉(zhuǎn)為字符串瞻讽!
因此,Java禁止型變以保證運行時的安全,但這樣會有一些影響,
例如,假設(shè)Collection.addAll()參數(shù)如下:
// Java
interface Collection<E> …… {
void addAll(Collection<E> items);
}
void copyAll(Collection<Object> to, Collection<String> from) {
//addAll不能編譯,Collection<String>不是Collection<Object>子類型
to.addAll(from);
}
這就是為什么Collection.addAll()實際參數(shù)如下:
// Java
interface Collection<E> …… {
//通配符<? extends E>表示包括E在內(nèi)的所有子類,稱為協(xié)變(covariant)
//通配符<? super E>表示包括E在內(nèi)的所有父類,稱為逆變(contravariance)
void addAll(Collection<? extends E> items);
}
void copyAll(Collection<Object> to, Collection<String> from) {
//<? extends E>可以讓Collection<String>是Collection<? extends Object>子類型
to.addAll(from);
}
<? extends E>協(xié)變(covariant): 表示包括E在內(nèi)的所有子類,泛型對象只能讀取,稱為生產(chǎn)者
<? super E>逆變(contravariance): 表示包括E在內(nèi)的所有父類,泛型對象只能寫入,稱為消費者
助記符:Producer-extends, Consumer-super
3.聲明處型變(Declaration-site variance)
Java泛型的一個例子:
interface Source<T> {
//只有生產(chǎn)者方法,沒有消費者方法
T nextT();
}
void demo(Source<String> strs) {
//Source<T>沒有消費者方法,型變是安全的,但是Java并不知道,所以仍然禁止!
//需要聲明類型為Source<? extends Object>,這是毫無意義的,更復(fù)雜類型并沒有帶來價值!
Source<Object> objects = strs; //錯誤:在Java中不允許型變
}
1.out修飾符
在Kotlin中,可用out修飾類型參數(shù)T,確保T只能輸出(生產(chǎn)),不被消費!
out修飾符稱為型變注解(variance annotation),使類型參數(shù)協(xié)變(covariant)!
abstract class Source<out T> {
abstract fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs //可以型變,因為T是out
}
2.in修飾符
用in修飾類型參數(shù)T,確保T只能被消費,不能輸出(生產(chǎn)),使類型參數(shù)逆變(contravariance)!
abstract class Comparable<in T> {
abstract fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
//1.0擁有Double類,是Number的子類
x.compareTo(1.0)
//Double是Number的子類,父類Number可以被Double消費
val y: Comparable<Double> = x
}
助記符:消費者-輸入in, 生產(chǎn)者-輸出out
由于in/out在類型參數(shù)聲明處,所以稱為聲明處型變(Declaration-site variance)
4.使用處型變(Use-site variance)/類型投影(Type projections)
類型參數(shù)T既不是協(xié)變,也不是逆變(T既生產(chǎn)out,又消費in):
class Array<T>(val size: Int) {
fun get(index: Int): T {...} //生產(chǎn)out
fun set(index: Int, value: T) {...} //消費in
}
fun copy(from: Array<Any>, to: Array<Any>) {
assert(from.size == to.size)
for (i in from.indices)
to[i] = from[i]
}
val ints: Array<Int> = arrayOf(1, 2, 3)
val anys = Array<Any>(3) { "" }
copy(ints, anys) //錯誤:期望(Array<Any>, Array<Any>)
1.out,確保from中的Any只生產(chǎn)輸出,不被消費,對應(yīng)于Java的Array<? extends Object>:
fun copy(from: Array<out Any>, to: Array<Any>) {
...
}
2.in,確保dest中的String只能被消費,不生產(chǎn)輸出,對應(yīng)于Java的Array<? super String>
fun fill(dest: Array<in String>, value: String) {
...
}
5.星投影<*>(Star-projections)
如果對類型參數(shù)一無所知,可用星投影:
1.對于Foo<out T>,T是一個具有上界TUpper的協(xié)變類型參數(shù),Foo<*>等價于Foo<out TUpper>,
當(dāng)T未知時,可以安全地從Foo<*>讀取TUpper的值
2.對于Foo<in T>,T是一個逆變類型參數(shù),Foo<*>等價于Foo<in Nothing>,
當(dāng)T未知時,沒有什么方式可以安全寫入Foo<*>
3.對于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的原始類型,但是安全!
6.泛型函數(shù)
和java類似, kotling不僅類有泛型,函數(shù)也有泛型:
//普通函數(shù)
fun <T> singletonList(item: T): List<T> {
// ……
}
//擴展函數(shù)
fun <T> T.basicToString() : String {
// ……
}
//調(diào)用泛型函數(shù),在函數(shù)名后指定類型參數(shù)
val l = singletonList<Int>(1)
7.泛型約束
最常見的約束類型是,與Java的<? extends T>對應(yīng)的上界:
//<T : Comparable<T>>冒號之后指定類型上界,只有Comparable<T>子類型可以替代T
fun <T : Comparable<T>> sort(list: List<T>) {
}
sort(listOf(1, 2, 3)) //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() }
}
簡書:http://www.reibang.com/p/e9e743baa77a
CSDN博客: http://blog.csdn.net/qq_32115439/article/details/73656998
GitHub博客:http://lioil.win/2017/06/23/Kotlin-generics.html
Coding博客:http://c.lioil.win/2017/06/23/Kotlin-generics.html