1.泛型:
Kotlin中也有泛型,和Java類似
例如:
class Person <T>(name: T){
var personName = name
}
這個類的主構造函數,初始化時需要指定泛型,
下面是測試:
val person = Person<String>("ckw")
在這裏我們指定了主構造函數中的參數的類型是String,
person.personName
這裏的值就是”ckw”
但是如果類型參數可以推斷出來,例如從構造函數的參數或者從其他途徑,允許省略類型參數
例如我們可以這樣寫:
val person = Person("ckw")
由於可以推斷出參數的類型是String類型的,所以T的類型可以省略。
2.型變
Java 類型系統中最棘手的部分之一是通配符類型,通配符類型參數 ? extends E
表示此方法接受 E 或者 E 的 一些子類型對象的集合,而不只是 E 自身
Java 中的泛型是不型變的,這意味着 List<String>
並不是List<Object>
的子類型。
爲什麼這樣? 如果 List 是型變的,如下代碼會通過編譯然後導致運行時異常:
List<String> strs = new ArrayList<String>();
List<Object> objs = strs; // !!!即將來臨的問題的原因就在這裏。Java 禁止這樣!
objs.add(1); // 這裏我們把一個整數放入一個字符串列表
String s = strs.get(0); // !!! ClassCastException:無法將整數轉換爲字符串
哎,關於型變,我覺得文檔上翻譯地有一點多,Java部分的我就不寫了,直接看Kotlin部分。簡而言之,Kotlin對於型變這塊,對其進行了升級。
直接來看Kotlin的概念:聲明處型變
首先看一下它是爲了解決什麼問題而提出來的:
假設有一個泛型接口 Source,該接口中不存在任何以 T 作爲參數的方法,只是方法返回 T 類型值:
interface Source<T> {
T outT();
}
那麼,在 Source 類型的變量中存儲 Source 實例的引用是極爲安全的。但是 Java 並不知道這一點,並且仍然禁止這樣操作:
void demo(Source<String> strs) {
Source<Object> objects = strs; // !!!在 Java 中不允許
}
爲此,Kotlin中提供了兩個修飾符:in 和 out
下面來看一下out
解釋:當一個類 C 的類型參數 T 被聲明爲 out 時,它就只能出現在 C 的成員的輸出位置,但回報是 C 可以安全地作爲 C的超類。
簡而言之,他們說類 C 是在參數 T 上是協變的,或者說 T 是一個協變的類型參數。 你可以認爲 C 是 T 的生產者,而不是 T 的消費者。
例子:
interface Source<out T> {
fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // 這個沒問題,因爲 T 是一個 out-參數
// ……
}
in
使得一個類型參數逆變:只可以被消費而不可以被生產。逆變類型的一個很好的例子是 Comparable:
interface Comparable<in T> {
operator fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0 擁有類型 Double,它是 Number 的子類型
// 因此,我們可以將 x 賦給類型爲 Comparable <Double> 的變量
val y: Comparable<Double> = x // OK!
}
我個人的理解是標記爲out的類型只能用作輸出,例如一個函數的返回值,in的類型只能作爲函數的輸入,例如函數的參數,這一塊個人理解的還不夠透徹,一知半解吧,需要以後在實踐中應用加深理解!
3.泛型函數:
不僅類可以有類型參數。函數也可以有。類型參數要放在函數名稱之前:
fun <T> singletonList(item: T): List<T> {
// ……
}
fun <T> T.basicToString() : String { // 擴展函數
// ……
}
要調用泛型函數,在調用處函數名之後指定類型參數即可:
val l = singletonList<Int>(1)
4.泛型約束
Kotlin中的冒號 :
最常見的約束類型是與 Java 的 extends 關鍵字對應的 上界:
默認的上界(如果沒有聲明)是 Any?。在尖括號中只能指定一個上界。 如果同一類型參數需要多個上界,我們需要一個單獨的 where-子句:
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
where T : CharSequence,
T : Comparable<T> {
return list.filter { it > threshold }.map { it.toString() }
}
哎,學習之路任重道遠,這塊內容還是要多學習學習