中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Kotlin 泛型詳解及簡單實例

發布時間:2020-10-07 10:28:07 來源:腳本之家 閱讀:183 作者:行云間 欄目:編程語言

 Kotlin 泛型詳解

概述

一般類和函數,只能使用具體的類型:要么是基本類型,要么是自定義的類。如果要編寫可以應用于多種類型的代碼,這種刻板的約束對代碼的限制很大。而OOP的多態采用了一種泛化的機制,在SE 5種,Java引用了泛型。泛型,即“參數化類型”。一提到參數,最熟悉的就是定義方法時有形參,然后調用此方法時傳遞實參。那么參數化類型怎么理解呢?顧名思義,就是將類型由原來的具體的類型參數化,類似于方法中的變量參數,此時類型也定義成參數形式(可以稱之為類型形參),然后在使用/調用時傳入具體的類型(類型實參)。

在Kotlin中,依然可以使用泛型,解耦類與函數與所用類型之間的約束,甚至是使用方法都與Java一致。

泛型類

聲明一個泛型類

class Box<T>(t: T) {
  var value = t
}

通常, 要創建這樣一個類的實例, 我們需要指定類型參數:

val box: Box<Int> = Box<Int>(1)

但是, 如果類型參數可以通過推斷得到, 比如, 通過構造器參數類型, 或通過其他手段推斷得到, 此時允許省略類型參數:

val box = Box(1) // 1 的類型為 Int, 因此編譯器知道我們創建的實例是 Box&lt;Int> 類型

泛型函數

泛型函數與其所在的類是否是泛型沒有關系。泛型函數使得該函數能夠獨立于其所在類而產生變化。在<Thinking in Java>有這么一句話:無論何時只要你能做到,你就應該盡量使用泛型方法,也就是說如果使用泛型方法可以取代將整個類泛型化,那么就應該只使用泛型方法,因為它可以使事情更明白。這種泛型使用思想,在Kotlin中依然可以延續。

下面我們聲明了一個泛型函數doPrintln,當T是一個Int類型時,打印其個位的值;如果T是String類型,將字母全部大寫輸出;如果是其他類型,打印“T is not Int and String”。

fun main(args: Array<String>) {
  val age = 23
  val name = "Jone"
  val person = true

  doPrintln(age) // 打印:3
  doPrintln(name) // 打印:JONE
  doPrintln(person) // 打印:T is not Int and String
}

fun <T> doPrintln(content: T) {

  when (content) {
    is Int -> println(content % 10) 
    is String -> println(content.toUpperCase())
    else -> println("T is not Int and String")
  }
}

注:

  1. 類型參數放在函數名稱之前。
  2. 如果在調用處明確地傳入了類型參數, 那么類型參數應該放在函數名稱 之后。如果不傳入參數類型,編譯器會根據傳入的值自動推斷參數類型。

擦除的神秘之處

下面我們先看一段代碼:

class Box<T>(t : T) {
  var value = t
}


fun main(args: Array<String>) {
  var boxInt = Box<Int>(10)
  var boxString = Box<String>("Jone")

  println(boxInt.javaClass) // 打印:class com.teaphy.generic.Box
  println(boxString.javaClass) // 打印:class com.teaphy.generic.Box
}

現聲明了一個泛型類Box<T>,在不同的類型的類型在行為方面肯定不一樣,但是在我們獲取其所在類時,我們只是得到了“class com.teaphy.generic.Box”。在這里我們不得不面對一個殘酷的現實:在泛型內部,無法獲得任何有關泛型參數類型的信息。

不管是Java還是Kotlin,泛型都是使用擦除來實現的,這意味著當你在使用泛型時,任務具體的類型信息都被擦除的,你唯一知道的就是你再使用一個對象。比如,Box<String>和Box<Int>在運行時是想的類型,都是Box的實例。在使用泛型時,具體類型信息的擦除是我們不不懂得不面對的,在Kotlin中也為我們提供了一些可供參考的解決方案:

  • 類型協變
  • 類型投射
  • 泛型約束

類型協變

在類型聲明時,使用協變注解修飾符(in或者out)。于這個注解出現在類型參數的聲明處, 因此我們稱之為聲明處的類型變異。如果在使用泛型時,使用了該類型編譯了會有什么效果呢?

假設我們有一個泛型接口Source<in T, out R>, 其中T由協變注解in修飾,R由協變注解Out修飾.

internal interface Source<in T, out R> {
  fun mapT(t: T): Unit
  fun nextR(): R
}
  • in T: 來確保Source的成員函數只能消費T類型,而不能返回T類型
  • out R:來確保Source的成員函數只能返回R類型,而不能消費R類型

從上面的解釋中,我們可以清楚的知道了協變注解in和out的用意,其實際上是定義了類型參數在該類或者接口的用途,是用來消費的還是用來返回的,對其做了相應的限定。

類型投射

上面我們已經了解到了協變注解in和out的用意,下面我們將會用in和out,做一件有意義的事,看下面代碼

fun copy(from: Array<out String>, to: Array<Any>) {
  // ...
}

fun fill(dest: Array<in String>, value: String) {
  // ...
}

對于copy函數中中,from的泛型參數使用了協變注解out修飾,意味著該參數不能在該函數中消費,也就是說在該函數中禁止對該參數進行任何操作。

對于fill函數中,dest的泛型參數使用了協變注解in修飾,Array<in String>與Java的 Array<? super String> 相同, 也就是說, 你可以使用CharSequence數組,或者 Object 數組作為 fill() 函數的參數

這種聲明在Kotlin中稱為類型投射(type projection),類型投射的主要用于對參數做了相對因的限定,避免了對該參數類的不安全操作。

星號投射

有些時候, 你可能想表示你并不知道類型參數的任何信息, 但是仍然希望能夠安全地使用它. 這里所謂”安全地使用”是指, 對泛型類型定義一個類型投射, 要求這個泛型類型的所有的實體實例, 都是這個投射的子類型.

對于這個問題, Kotlin 提供了一種語法, 稱為 星號投射(star-projection):

  1. 假如類型定義為 Foo<out T> , 其中 T 是一個協變的類型參數, 上界(upper bound)為 TUpper ,Foo<> 等價于 Foo<out TUpper> . 它表示, 當 T 未知時, 你可以安全地從 Foo<> 中 讀取TUpper 類型的值.
  2. 假如類型定義為 Foo<in T> , 其中 T 是一個反向協變的類型參數, Foo<> 等價于 Foo<inNothing> . 它表示, 當 T 未知時, 你不能安全地向 Foo<> 寫入 任何東西.
  3. 假如類型定義為 Foo<T> , 其中 T 是一個協變的類型參數, 上界(upper bound)為 TUpper , 對于讀取值的場合, Foo<*> 等價于 Foo<out TUpper> , 對于寫入值的場合, 等價于 Foo<in Nothing> .

如果一個泛型類型中存在多個類型參數, 那么每個類型參數都可以單獨的投射. 比如, 如果類型定義為interface Function<in T, out U> , 那么可以出現以下幾種星號投射:

  1. Function<*, String> , 代表 Function<in Nothing, String> ;
  2. Function<Int, *> , 代表 Function<Int, out Any?> ;
  3. Function<, > , 代表 Function<in Nothing, out Any?> .

注意: 星號投射與 Java 的原生類型(raw type)非常類似, 但可以安全使用

泛型約束

對于一個給定的類型參數, 所允許使用的類型, 可以通過泛型約束(generic constraint) 來限制。

上界

最常見的約束是 上界(upper bound):

fun <T : Comparable<T>> sort(list: List<T>) {
  // ...
}

冒號之后指定的類型就是類型參數的 上界(upper bound): 對于類型參數 T , 只允許使用 Comparable<T>的子類型. 比如:

sort(listOf(1, 2, 3)) // 正確: Int 是 Comparable&lt;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() }
}

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

介休市| 蚌埠市| 乌兰察布市| 海盐县| 济源市| 东兰县| 会宁县| 新民市| 达拉特旗| 石景山区| 舟曲县| 聂拉木县| 东阳市| 扎兰屯市| 大冶市| 阿克陶县| 嘉祥县| 洱源县| 额尔古纳市| 丘北县| 海淀区| 南陵县| 将乐县| 罗山县| 曲水县| 富平县| 高清| 新绛县| 土默特右旗| 江安县| 礼泉县| 思南县| 宁陕县| 平江县| 丰城市| 沾化县| 明溪县| 岳普湖县| 都兰县| 咸宁市| 行唐县|