新聞中心
Kotlin 泛型詳解
創(chuàng)新互聯(lián)建站長期為千余家客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為蘇州企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計、網(wǎng)站制作,蘇州網(wǎng)站改版等技術(shù)服務(wù)。擁有十余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
概述
一般類和函數(shù),只能使用具體的類型:要么是基本類型,要么是自定義的類。如果要編寫可以應(yīng)用于多種類型的代碼,這種刻板的約束對代碼的限制很大。而OOP的多態(tài)采用了一種泛化的機制,在SE 5種,Java引用了泛型。泛型,即“參數(shù)化類型”。一提到參數(shù),最熟悉的就是定義方法時有形參,然后調(diào)用此方法時傳遞實參。那么參數(shù)化類型怎么理解呢?顧名思義,就是將類型由原來的具體的類型參數(shù)化,類似于方法中的變量參數(shù),此時類型也定義成參數(shù)形式(可以稱之為類型形參),然后在使用/調(diào)用時傳入具體的類型(類型實參)。
在Kotlin中,依然可以使用泛型,解耦類與函數(shù)與所用類型之間的約束,甚至是使用方法都與Java一致。
泛型類
聲明一個泛型類
class Box(t: T) { var value = t }
通常, 要創(chuàng)建這樣一個類的實例, 我們需要指定類型參數(shù):
val box: Box= Box (1)
但是, 如果類型參數(shù)可以通過推斷得到, 比如, 通過構(gòu)造器參數(shù)類型, 或通過其他手段推斷得到, 此時允許省略類型參數(shù):
val box = Box(1) // 1 的類型為 Int, 因此編譯器知道我們創(chuàng)建的實例是 Box<Int> 類型
泛型函數(shù)
泛型函數(shù)與其所在的類是否是泛型沒有關(guān)系。泛型函數(shù)使得該函數(shù)能夠獨立于其所在類而產(chǎn)生變化。在
下面我們聲明了一個泛型函數(shù)doPrintln,當(dāng)T是一個Int類型時,打印其個位的值;如果T是String類型,將字母全部大寫輸出;如果是其他類型,打印“T is not Int and String”。
fun main(args: Array) { val age = 23 val name = "Jone" val person = true doPrintln(age) // 打印:3 doPrintln(name) // 打?。篔ONE doPrintln(person) // 打印:T is not Int and String } fun doPrintln(content: T) { when (content) { is Int -> println(content % 10) is String -> println(content.toUpperCase()) else -> println("T is not Int and String") } }
注:
- 類型參數(shù)放在函數(shù)名稱之前。
- 如果在調(diào)用處明確地傳入了類型參數(shù), 那么類型參數(shù)應(yīng)該放在函數(shù)名稱 之后。如果不傳入?yún)?shù)類型,編譯器會根據(jù)傳入的值自動推斷參數(shù)類型。
擦除的神秘之處
下面我們先看一段代碼:
class Box(t : T) { var value = t } fun main(args: Array ) { var boxInt = Box (10) var boxString = Box ("Jone") println(boxInt.javaClass) // 打印:class com.teaphy.generic.Box println(boxString.javaClass) // 打?。篶lass com.teaphy.generic.Box }
現(xiàn)聲明了一個泛型類Box
不管是Java還是Kotlin,泛型都是使用擦除來實現(xiàn)的,這意味著當(dāng)你在使用泛型時,任務(wù)具體的類型信息都被擦除的,你唯一知道的就是你再使用一個對象。比如,Box
- 類型協(xié)變
- 類型投射
- 泛型約束
類型協(xié)變
在類型聲明時,使用協(xié)變注解修飾符(in或者out)。于這個注解出現(xiàn)在類型參數(shù)的聲明處, 因此我們稱之為聲明處的類型變異。如果在使用泛型時,使用了該類型編譯了會有什么效果呢?
假設(shè)我們有一個泛型接口Source
internal interface Source{ fun mapT(t: T): Unit fun nextR(): R }
- in T: 來確保Source的成員函數(shù)只能消費T類型,而不能返回T類型
- out R:來確保Source的成員函數(shù)只能返回R類型,而不能消費R類型
從上面的解釋中,我們可以清楚的知道了協(xié)變注解in和out的用意,其實際上是定義了類型參數(shù)在該類或者接口的用途,是用來消費的還是用來返回的,對其做了相應(yīng)的限定。
類型投射
上面我們已經(jīng)了解到了協(xié)變注解in和out的用意,下面我們將會用in和out,做一件有意義的事,看下面代碼
fun copy(from: Array, to: Array ) { // ... } fun fill(dest: Array , value: String) { // ... }
對于copy函數(shù)中中,from的泛型參數(shù)使用了協(xié)變注解out修飾,意味著該參數(shù)不能在該函數(shù)中消費,也就是說在該函數(shù)中禁止對該參數(shù)進行任何操作。
對于fill函數(shù)中,dest的泛型參數(shù)使用了協(xié)變注解in修飾,Array
這種聲明在Kotlin中稱為類型投射(type projection),類型投射的主要用于對參數(shù)做了相對因的限定,避免了對該參數(shù)類的不安全操作。
星號投射
有些時候, 你可能想表示你并不知道類型參數(shù)的任何信息, 但是仍然希望能夠安全地使用它. 這里所謂”安全地使用”是指, 對泛型類型定義一個類型投射, 要求這個泛型類型的所有的實體實例, 都是這個投射的子類型.
對于這個問題, Kotlin 提供了一種語法, 稱為 星號投射(star-projection):
- 假如類型定義為 Foo
, 其中 T 是一個協(xié)變的類型參數(shù), 上界(upper bound)為 TUpper ,Foo<> 等價于 Foo . 它表示, 當(dāng) T 未知時, 你可以安全地從 Foo<> 中 讀取TUpper 類型的值. - 假如類型定義為 Foo
, 其中 T 是一個反向協(xié)變的類型參數(shù), Foo<> 等價于 Foo . 它表示, 當(dāng) T 未知時, 你不能安全地向 Foo<> 寫入 任何東西. - 假如類型定義為 Foo
, 其中 T 是一個協(xié)變的類型參數(shù), 上界(upper bound)為 TUpper , 對于讀取值的場合, Foo<*> 等價于 Foo , 對于寫入值的場合, 等價于 Foo .
如果一個泛型類型中存在多個類型參數(shù), 那么每個類型參數(shù)都可以單獨的投射. 比如, 如果類型定義為interface Function
- Function<*, String> , 代表 Function
; - Function
, 代表 Function ; - Function<, > , 代表 Function
.
注意:星號投射與 Java 的原生類型(raw type)非常類似, 但可以安全使用
泛型約束
對于一個給定的類型參數(shù), 所允許使用的類型, 可以通過泛型約束(generic constraint) 來限制。
上界
最常見的約束是 上界(upper bound):
fun> sort(list: List ) { // ... }
冒號之后指定的類型就是類型參數(shù)的 上界(upper bound): 對于類型參數(shù) T , 只允許使用 Comparable
sort(listOf(1, 2, 3)) // 正確: Int 是 Comparable<Int> 的子類型 sort(listOf(HashMap())) // 錯誤: HashMap 不是 Comparable > 的子類型
如果沒有指定, 則默認使用的上界是 Any? . 在定義類型參數(shù)的尖括號內(nèi), 只允許定義唯一一個上界. 如果同一個類型參數(shù)需要指定多個上界, 這時就需要使用單獨的 where 子句:
funcloneWhenGreater(list: List , threshold: T): List where T : Comparable, T : Cloneable { return list.filter { it > threshold }.map { it.clone() } }
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
分享標題:Kotlin泛型詳解及簡單實例
當(dāng)前路徑:http://fisionsoft.com.cn/article/gjiohp.html