我的Kotlin學習之旅(七)

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中提供了兩個修飾符:inout
下面來看一下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() }
}

哎,學習之路任重道遠,這塊內容還是要多學習學習

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章