做基礎(chǔ)網(wǎng)站主機(jī)要關(guān)鍵詞優(yōu)化方法
Kotlin泛型的概念及使用
泛型概念
在Kotlin中,泛型(Generics)是一種允許在類、接口和方法中使用類型參數(shù)的技術(shù)。這些類型參數(shù)在實(shí)例化類、實(shí)現(xiàn)接口或調(diào)用方法時(shí)會(huì)被具體的類型所替代。泛型的主要目的是提高代碼的復(fù)用性、類型安全性和可讀性。
泛型使用
- 泛型類
定義一個(gè)泛型類,可以在類名后面加上尖括號(hào)< >
,并在其中聲明類型參數(shù)。
class Box<T>(val item: T) { | |
fun getContent(): T { | |
return item | |
} | |
} | |
// 使用時(shí)指定類型參數(shù) | |
val intBox = Box<Int>(10) | |
val stringBox = Box<String>("Hello") |
- 泛型函數(shù)
函數(shù)也可以有類型參數(shù)。
fun <T> printItems(items: List<T>) { | |
for (item in items) { | |
print(item) | |
print(", ") | |
} | |
println() | |
} | |
// 使用時(shí),Kotlin會(huì)自動(dòng)推斷T的類型 | |
printItems(listOf(1, 2, 3)) | |
printItems(listOf("a", "b", "c")) |
- 泛型接口
與泛型類和泛型函數(shù)類似,接口也可以有類型參數(shù)。
interface Listener<T> { | |
fun onItemClicked(item: T) | |
} | |
// 實(shí)現(xiàn)泛型接口 | |
class ButtonClickListener<T> : Listener<T> { | |
override fun onItemClicked(item: T) { | |
// 處理點(diǎn)擊事件 | |
} | |
} |
協(xié)變(Covariance)
協(xié)變是指在一個(gè)泛型類型中,如果類型參數(shù)是某個(gè)類的子類型,那么使用這個(gè)類型參數(shù)的泛型類型也應(yīng)該是父類泛型類型的子類型。在Kotlin中,通過(guò)out
修飾符實(shí)現(xiàn)協(xié)變。
interface Source<out T> { | |
fun next(): T? | |
} | |
fun demo(strs: Source<String>) { | |
// ... | |
} | |
val intSource: Source<Int> = ... | |
// 因?yàn)镮nt是String的子類型(在Kotlin中String不是Int的子類,這里僅作示例),但Source<Int>不是Source<String>的子類型 | |
// 所以不能直接傳遞intSource給demo函數(shù),但可以通過(guò)協(xié)變實(shí)現(xiàn) | |
demo(intSource as Source<String>) // 錯(cuò)誤:類型不匹配 | |
// 正確的協(xié)變用法 | |
val stringSource: Source<out String> = intSource as? Source<out String> // 這里假設(shè)intSource實(shí)際上可以轉(zhuǎn)換為Source<out String> | |
if (stringSource != null) { | |
demo(stringSource) // 正確 | |
} |
注意:在Kotlin中,String
并不是Int
的子類型,上面的例子僅用于說(shuō)明協(xié)變的概念。
逆變(Contravariance)
逆變與協(xié)變相反,它指的是在一個(gè)泛型類型中,如果類型參數(shù)是某個(gè)類的父類型,那么使用這個(gè)類型參數(shù)的泛型類型也應(yīng)該是子類泛型類型的父類型。在Kotlin中,通過(guò)in
修飾符實(shí)現(xiàn)逆變。
interface Sink<in T> { | |
fun put(item: T) | |
} | |
fun fill(sink: Sink<Number>) { | |
// ... | |
} | |
val stringSink: Sink<String> = ... | |
// 因?yàn)镾tring是Number的子類型,但Sink<String>不是Sink<Number>的子類型 | |
// 所以不能直接傳遞stringSink給fill函數(shù),但可以通過(guò)逆變實(shí)現(xiàn) | |
fill(stringSink as Sink<Number>) // 錯(cuò)誤:類型不匹配 | |
// 正確的逆變用法 | |
val numberSink: Sink<in Number> = stringSink as? Sink<in Number> // 這里假設(shè)stringSink實(shí)際上可以轉(zhuǎn)換為Sink<in Number> | |
if (numberSink != null) { | |
fill(numberSink) // 正確 | |
} |
同樣,上面的例子僅用于說(shuō)明逆變的概念,實(shí)際上String
不是Number
的子類型。
星號(hào)投射(Star Projection)
星號(hào)投射(*
)在Kotlin中用于處理泛型類型的通配符情況。當(dāng)你聲明一個(gè)泛型類型但不想指定具體的類型參數(shù)時(shí),可以使用星號(hào)投射。
使用方式:
-
協(xié)變星號(hào)投射:
List<out T*>
?通常簡(jiǎn)化為?List<*>
。這表示列表中的元素可以是任何類型,但當(dāng)你從列表中取出元素時(shí),它的類型會(huì)被視為Any?
(因?yàn)槿魏晤愋投伎梢再x值給Any?
)。
val list: List<*> = ... // list可以是任何類型的List | |
for (item in list) { | |
if (item is String) { | |
println(item.length) // 只有在確定item是String類型時(shí)才能調(diào)用其方法 | |
} | |
} |
- 逆變星號(hào)投射:在Kotlin中,逆變星號(hào)投射不常用,因?yàn)镵otlin的泛型系統(tǒng)主要基于協(xié)變和不變。但在某些高級(jí)用法中,你可能會(huì)遇到類似于
Sink<in T*>
的逆變星號(hào)投射,這表示該接口或類可以接受任何類型的參數(shù)。
委托(Delegation)
概念:
委托(Delegation)是一種設(shè)計(jì)模式,它允許一個(gè)對(duì)象(委托對(duì)象)將其職責(zé)的一部分或全部委托給另一個(gè)對(duì)象(被委托對(duì)象)。委托模式可以提高代碼的復(fù)用性和可維護(hù)性。
使用:
-
類委托:在Kotlin中,可以使用
by
關(guān)鍵字來(lái)實(shí)現(xiàn)類委托。這允許一個(gè)類將某些方法的實(shí)現(xiàn)委托給另一個(gè)類的實(shí)例。
class Base { | |
fun printMessage() { | |
println("Message from Base") | |
} | |
} | |
class Derived(b: Base) : Base() by b { | |
// Derived類將Base類的printMessage方法委托給b實(shí)例 | |
} | |
fun main() { | |
val derived = Derived(Base()) | |
derived.printMessage() // 輸出 "Message from Base" | |
} |
注意:在上面的例子中,Derived
類繼承了Base
類,但實(shí)際上并沒(méi)有重寫(xiě)printMessage
方法。相反,它使用by
關(guān)鍵字將該方法的調(diào)用委托給了b
實(shí)例(即Base
類的一個(gè)實(shí)例)。
2.?屬性委托:Kotlin還支持屬性委托,允許你將屬性的get
和set
操作委托給另一個(gè)對(duì)象或表達(dá)式。這可以通過(guò)在屬性聲明中使用by
關(guān)鍵字和相應(yīng)的委托提供程序來(lái)實(shí)現(xiàn)。
class LazyValue<T>(private val initializer: () -> T) { | |
private var value: T? = null | |
fun getValue(): T { | |
if (value == null) { | |
value = initializer() | |
} | |
return value!! | |
} | |
// 這里省略了setValue方法,因?yàn)槲覀冎魂P(guān)心只讀屬性 | |
} | |
class Example { | |
val lazyString: String by LazyValue { "Hello, World!" } | |
} | |
fun main() { | |
val example = Example() | |
println(example.lazyString) // 輸出 "Hello, World!",并且只會(huì)在第一次訪問(wèn)時(shí)計(jì)算值 | |
} |
在這個(gè)例子中,lazyString
屬性的get
操作被委托給了LazyValue
類的實(shí)例。當(dāng)?shù)谝淮卧L問(wèn)lazyString
時(shí),它會(huì)調(diào)用LazyValue
的getValue
方法來(lái)計(jì)算并緩存值。之后的訪問(wèn)將直接返回緩存的值。