Kotlin語言基礎(chǔ)筆記
Kotlin流程控制語句筆記
Kotlin操作符重載與中綴表示法筆記
Kotlin擴(kuò)展函數(shù)和擴(kuò)展屬性筆記
Kotlin空指針安全(null-safety)筆記
Kotlin類型系統(tǒng)筆記
Kotlin面向?qū)ο缶幊坦P記
Kotlin委托(Delegation)筆記
Kotlin泛型型筆記
Kotlin函數(shù)式編程筆記
Kotlin與Java互操作筆記
Kotlin協(xié)程筆記
Kotlin中的泛型跟Java中的泛型基本上一樣,都是在編譯的時(shí)候才有效果,在運(yùn)行期是擦除的。我們可以在類,方法中使用泛型:
class Box<T>(var value: T){
fun get(): T {
return value
}
fun set(x: T){
this.value = x;
}
}
fun <T> newBox(value: T) = Box(value)
fun main(args: Array<String>) {
val box1:Box<String> = Box<String>("Denny")
val box2:Box<String> = Box("Denny")
val box3 = Box("Denny")
val box5 = newBox(true)
val box4 = Box(1)
box4.set("Jack") //編譯錯(cuò)誤。Type mismatch, Required: Int, Found String
}
泛型類的定義是在類名稱之后主構(gòu)造函數(shù)之前,泛型函數(shù)聲明跟Java一樣,類型參數(shù)要放在函數(shù)明之前。類型如果可以推斷出來的話,可以省略泛型類型。
泛型函數(shù)類型參數(shù)可以使用:
冒號(hào)指定上界:
fun <T : Comparable<T>> sort(list: List<T>){...}
如果有多個(gè)上界,可以使用where
語句。
fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<T>
where T : Comparable, Cloneable {
return list.filter(it > threshold).map(it.clone())
}
1. 協(xié)變(covariant)和逆變(contravariant)
Number[] numbers = new Integer[]{1, 2, 3}; //works
List<Number> numberList = new ArrayList<Integer>(); //編譯錯(cuò)誤,暴類型不兼容錯(cuò)誤。
Java中,數(shù)組是協(xié)變的,而泛型不是協(xié)變的,也就是說Integer是Number的子類,數(shù)組類型Integer[]也是Number[]的子類,但是List<Integer>不是List<Number>的子類。
Animal類型是Dog類型的父類,假如Animal類型標(biāo)記為F(father),Dog類型標(biāo)記為C(child),我們用這個(gè)表示父子類型關(guān)系:F <| C。而對(duì)應(yīng)的泛型List<Animal>,List<Dog>,我們標(biāo)記為f<F>, f<C>。那么當(dāng) F <| C時(shí):
- 如果f<F> <| f<C>成立,那么f叫做協(xié)變;
- 如果f<C> <| f<F>成立,那么f叫著逆變;
- 如果以上兩種情況都不成立,那么叫著不可變;
Java的泛型是不變的。如果要實(shí)現(xiàn)協(xié)變和逆變的話,那么我們就需要使用通配符?
。
<? extends T>
實(shí)現(xiàn)了泛型的協(xié)變
List<? extends Number> list1 = new ArrayList<Integer>();
List<? extends Number> list2 = new ArrayList<Float>();
以上便實(shí)現(xiàn)了協(xié)變,但是你要注意的是,這里的list1和list2只能夠add(null),不能夠添加其他任意對(duì)象。如下:
為什么會(huì)這樣呢?因?yàn)槿绻鸏ist<? extends Number> 可以添加Integer,F(xiàn)loat,Double等數(shù)字類型的話,那么從這個(gè)List獲取到的值可能會(huì)有各種類型,也就會(huì)引發(fā)潛在的錯(cuò)誤,所以Java為了保護(hù)類型一致性,就禁止向List<? extends Number> 添加任意對(duì)象。
<? super T>實(shí)現(xiàn)了泛型的逆變
List<? super Number> list3 = new ArrayList<Number>();
List<? super Number> list4 = new ArrayList<Object>();
list3.add(new Integer(3));
list4.add(new Float((3.3)));
我們可以向List<? super Number>添加Number以及Number的子類,但是不能添加Number的父類。
PECS
PECS: producer-extends,consumer-super
也就是說生產(chǎn)者使用extends,消費(fèi)者使用super。具體可以看看java.util.Collections#copy
方法:
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator(); // in T, 寫入dest數(shù)據(jù)
ListIterator<? extends T> si=src.listIterator(); // out T, 讀取src數(shù)據(jù)
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}
2. Kotlin中的泛型
Kotlin中的泛型使用的正是前面說的PECS(生產(chǎn)者消費(fèi)者)概念,在上面說到的java.util.Collections#copy
方法中:
-
List<? extends T> src
是生產(chǎn)者,也就是數(shù)據(jù)提供者,在Kotlin中叫out T
。生產(chǎn)者不能寫入數(shù)據(jù),只能讀取。 -
List<? super T> dest
是消費(fèi)者,也就是吞掉一些數(shù)據(jù),或者說會(huì)有數(shù)據(jù)寫入到該對(duì)象中,在Kotlin中叫in T
,消費(fèi)者可以寫入T或者T的子類。
在Kotlin中,我們把那些只能保證讀取數(shù)據(jù)時(shí)類型安全的對(duì)象叫做生產(chǎn)者,用 out T標(biāo)記;把那些只能保證寫入數(shù)據(jù)安全時(shí)類型安全的對(duì)象叫做消費(fèi)者,用 in T標(biāo)記。
2.1 聲明處型變
Kotlin對(duì)Java泛型最大的改動(dòng)就是增加了聲明處型變,下面的例子:
interface Source<T> {
T nextT();
}
void demo(Source<String> str) {
// Java 中這種寫法是不允許的
Source<Object> obj = str;
/*...*/
}
因?yàn)镴ava泛型不可型變的,所以Source<String>不是Source<Object>的子類。但是在Kotlin中你就能做到,用Kotin改寫下:
interface Source<out T>{
fun nextT() : T
}
fun demo(str: Source<String>) {
val obj:Source<Any> = str //works
}
因?yàn)镾ource這個(gè)接口只有一個(gè)讀取數(shù)據(jù)的方法,可以視為生產(chǎn)者,所以把這個(gè)接口的類型參數(shù)聲明為生產(chǎn)者后,就可以實(shí)現(xiàn)安全的類型協(xié)變了。Kotlin中有大量的聲明處協(xié)變,比如:Iterable接口聲明:
public interface Iterable<out T> {
/**
* Returns an iterator over the elements of this object.
*/
public operator fun iterator(): Iterator<T>
}
因?yàn)镃ollection和Map接口都繼承了Iterable接口,而Iterable被聲明成了生產(chǎn)者(out T
)。所以Collection和Map以及子類都可以實(shí)現(xiàn)安全的類型協(xié)變:
val c: List<Number> = listOf(1, 2, 3)
2.2 類型投影
類似Java中的無界類型通配符?
, Kotlin 也有對(duì)應(yīng)的星投影語法*
。
例如,如果類型被聲明為 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 的原始類型類似,不過是安全的。