官方文檔: http://kotlinlang.org/docs/reference/generics.html
1.泛型(generics)
與Java類似,Kotlin的類也有類型參數(泛型):
class Box<T>(t: T) {
var value = t
}
一般情況,使用泛型實例,需要類型參數:
val box: Box<Int> = Box<Int>(1)
如果類型參數可推斷出來,可省略類型參數:
val box = Box(1) // 1是Int,編譯器可推斷出Box<Int>
2.型變(Variance)
Java泛型中最棘手部分就是通配符類型(初學實在頭暈),但kotlin沒有!
所以Kotlin通過型變(Variance)彌補:
聲明處型變(declaration-site variance)
類型投影(type projections)
為什么Java泛型需要通配符類型?
在Effective Java解釋了該問題—第28條:利用有限制通配符來提升API的靈活性。
Java泛型是不型變的,意味著List<String>不是List<Object>子類型!
如果List是型變的,如下代碼編譯正常,但運行時出現異常:
// Java
List<String> strs = new ArrayList<String>();
List<Object> objs = strs; //錯誤,Java禁止型變!
objs.add(1);
String s = strs.get(0); //運行出現異常ClassCastException:無法將整數轉為字符串!
因此,Java禁止型變以保證運行時的安全,但這樣會有一些影響,
例如,假設Collection.addAll()參數如下:
// 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()實際參數如下:
// Java
interface Collection<E> …… {
//通配符<? extends E>表示包括E在內的所有子類,稱為協變(covariant)
//通配符<? super E>表示包括E在內的所有父類,稱為逆變(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>協變(covariant): 表示包括E在內的所有子類,泛型對象只能讀取,稱為生產者
<? super E>逆變(contravariance): 表示包括E在內的所有父類,泛型對象只能寫入,稱為消費者
助記符:Producer-extends, Consumer-super
3.聲明處型變(Declaration-site variance)
Java泛型的一個例子:
interface Source<T> {
//只有生產者方法,沒有消費者方法
T nextT();
}
void demo(Source<String> strs) {
//Source<T>沒有消費者方法,型變是安全的,但是Java并不知道,所以仍然禁止!
//需要聲明類型為Source<? extends Object>,這是毫無意義的,更復雜類型并沒有帶來價值!
Source<Object> objects = strs; //錯誤:在Java中不允許型變
}
1.out修飾符
在Kotlin中,可用out修飾類型參數T,確保T只能輸出(生產),不被消費!
out修飾符稱為型變注解(variance annotation),使類型參數協變(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修飾類型參數T,確保T只能被消費,不能輸出(生產),使類型參數逆變(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, 生產者-輸出out
由于in/out在類型參數聲明處,所以稱為聲明處型變(Declaration-site variance)
4.使用處型變(Use-site variance)/類型投影(Type projections)
類型參數T既不是協變,也不是逆變(T既生產out,又消費in):
class Array<T>(val size: Int) {
fun get(index: Int): T {...} //生產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只生產輸出,不被消費,對應于Java的Array<? extends Object>:
fun copy(from: Array<out Any>, to: Array<Any>) {
...
}
2.in,確保dest中的String只能被消費,不生產輸出,對應于Java的Array<? super String>
fun fill(dest: Array<in String>, value: String) {
...
}
5.星投影<*>(Star-projections)
如果對類型參數一無所知,可用星投影:
1.對于Foo<out T>,T是一個具有上界TUpper的協變類型參數,Foo<*>等價于Foo<out TUpper>,
當T未知時,可以安全地從Foo<*>讀取TUpper的值
2.對于Foo<in T>,T是一個逆變類型參數,Foo<*>等價于Foo<in Nothing>,
當T未知時,沒有什么方式可以安全寫入Foo<*>
3.對于Foo<T>,T是一個具有上界TUpper的不型變類型參數,
Foo<*>在讀取值時等價于Foo<out TUpper>,在寫入值時等價于Foo<in Nothing>
如果有多個類型參數,則每個類型參數都可單獨投影:
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.泛型函數
和java類似, kotling不僅類有泛型,函數也有泛型:
//普通函數
fun <T> singletonList(item: T): List<T> {
// ……
}
//擴展函數
fun <T> T.basicToString() : String {
// ……
}
//調用泛型函數,在函數名后指定類型參數
val l = singletonList<Int>(1)
7.泛型約束
最常見的約束類型是,與Java的<? extends T>對應的上界:
//<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?,<:上界>中只能指定一個上界,如果同一類型參數需要多個上界,需要一個單獨的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.lxweimin.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