系列文章路引 👀 https://blog.csdn.net/weixin_44235109/article/details/119680974
文章目錄
-
- 系列文章路引 👀 https://blog.csdn.net/weixin_44235109/article/details/119680974
- 一、泛型的基本概念
-
-
- 泛型的概念
- 泛型的基本聲明方式
- 簡單例子:
-
- 二、泛型限制
-
-
- 一般限制
- 使用where關鍵字實作多個限制
-
- 多個限制實作
- 多個泛型參數實作
-
- 三、泛型形變
-
-
- 泛型形變三種形式
- 不變
- 協變
- 逆變
- UnSafeVariance
-
- 四、星投影 Star Projection
-
-
- 星投影
-
- 四、泛型的實作原理與内斂特化
-
-
- java與kotlin泛型實作原理簡介
- 内斂特化reified
- 内斂特化的簡單執行個體運用(簡單定義一個Gson的擴充函數吧)
-
- 總結
一、泛型的基本概念
泛型的概念
- 泛型是一種類型層面的抽象
- 泛型通過泛型參數實作構造更加通用的類型能力
- 泛型可以讓符合繼承關系的類型批量實作某些功能
泛型的基本聲明方式
簡單例子:
定義函數,比較任意類型的大小 <T> 表示聲明的泛型類型
inline fun <T> maxOf( a : T , b : T) : T
定義類,任意類型的清單 <T> 表示聲明的泛型類型
注意:第一個List和 下面 Cons繼承的List 這兩個T,不一樣,下面的是Cons類自己的泛型,然後傳給它繼承的List,即調用父類構造器傳上去的
sealed class DataList<T>{
object NIL:DataList<Nothing>()
data class Cons<T>(val head : T,val tail :DataList<T>) : DataList<T>()
}
//也可以寫為
sealed class DataList<T>{
object NIL:DataList<Nothing>()
data class Cons<R>(val head : R,val tail :DataList<R>) : DataList<R>()
}
二、泛型限制
一般限制
可以直接在聲明的泛型前面使用 “:” 加上被限制的類型,
比如我們完善一下上面說到的比較任意類型的大小。增加一個Comparable類型的限制,這麼就可以直接在代碼裡面進行比較了。
fun <T : Comparable<T>> maxOf(a: T, b: T): T {
return if (a > b) a else b
}
//使用
val value1 = maxOf(1, 2)
val value2 = maxOf(2.0, 3.0)
val value3 = maxOf("s", "ss")
使用where關鍵字實作多個限制
可以在聲明後面增加where關鍵字,進行多條件限制
多個限制實作
fun <T> callMax(a : T,b : T) where T : Comparable<T> , T : () -> Unit{
if (a > b) a else b
}
多個泛型參數實作
fun <T , R> callMax(a : T,b : T) : Rwhere T : Comparable<T> , T : () -> R{
if (a > b) a else b
}
例子:多個泛型參數,K要求可以被序列化,V要求可以比較
三、泛型形變
泛型形變三種形式
不變:<T> T前面沒有任何的修飾
協變:<out T> T前面使用out修飾,out表示協變
逆變:<in T> T前面使用in修飾,in表示逆變
不變
T前面沒有任何的修飾
注意此時:
List<Nothing>不是List<T>子類
List.NIL也不是List<T>的子類
sealed class DataList<T>{
object NIL:DataList<Nothing>()
...
}
協變
T前面使用out修飾,out表示協變
sealed class DataList<out T> {
fun test():T {
TODO()
}
}
- 子類Derived相容父類Base
- 生産者Producer<Derived>相容Producer<Base>
- 存在協變點的類的泛型參數必須聲明為協變或者不變
- 當泛型類作為泛型參數類執行個體的生産者時用協變
逆變
T前面使用in修飾,in表示逆變
這裡直接拿kotlin Comparable接口的源碼給大家看了
public interface Comparable<in T> {
/**
* Compares this object with the specified object for order. Returns zero if this object is equal
* to the specified [other] object, a negative number if it's less than [other], or a positive number
* if it's greater than [other].
*/
public operator fun compareTo(other: T): Int
}
- 子類Derived相容父類Base
- 生産者Consumer<Derived>相容Consumer<Base>
- 存在逆變點的類的泛型參數必須聲明為逆變或者不變
- 當泛型類作為泛型參數類執行個體的消費者時用逆變
UnSafeVariance
協變用到逆變點或者逆變用到協變點,可以使用@UnsafeVariance做保證
sealed class TList<out T>{
operator fun compareTo(other:@UnsafeVariance T):Int{
return 1
}
}
sealed class TList<in T>{
fun test():@UnsafeVariance T{
TODO()
}
}
違反形變限制的安全前提:
- 聲明為協變的類出現逆變點或者相反
- 聲明為不變的類接收逆變或協變的類型參數
- 泛型參數協變,逆變點不能引起修改,即始終隻讀不寫
- 泛型參數逆變,協變點不得外部擷取,即始終隻寫不讀
四、星投影 Star Projection
星投影
- ‘*’ 可用在變量類型聲明的位置
- ‘*’ 可用以描述一個未知的類型
- ‘*’ 所替換的類型在:協變點傳回參數的上限類型;逆變點接收泛型參數的下限類型。
協變點的使用
class QueryMap<out K : CharSequence, out V : Any>(val k: K, val v: V) {
fun getKey(): K {
return k
}
fun getValue(): V {
return v
}
}
// 星投影
val queryMap: QueryMap<*, *> = QueryMap("S", 1)
// CharSequence 類型
queryMap.getKey()
// Any 類型
queryMap.getValue()
逆變點的使用
逆變點星投影取下限,沒有對下限的明确定義,是以invoke隻能傳入nothing,但是nothing并沒有執行個體,是以并不能調用
class Funcation<in p1,in p2>{
fun invoke(p1: p1,p2: p2) = Unit
}
//調用
val f:Funcation<*,*> = Funcation<String,Number>()
f.invoke("ss",1)
比較适合作為描述類場景使用:如下
val queryMap: QueryMap<*, *>
if( is Function<*,*>){...}
HashMap<String,List<*>>()
四、泛型的實作原理與内斂特化
java與kotlin泛型實作原理簡介
首先知道一個概念:java和kotlin實作泛型的機制是僞泛型,編譯後實作類型擦除,進行類型了轉換。
//編譯前
fun <T:Comparable<T>> maxOf(a:T,b:T):T{
return if(a>b) a else b
}
//編譯後
fun maxOf(a:Comparable,b:Comparable):Comparable{
return if(a>b) a else b
}
常見的泛型實作原理
僞泛型:編譯時擦除類型,運作時無實際類型生成。列如:java、kotlin
真泛型:編譯時生成真實類型,運作時也存在該類。列如:c#、c++
是以,對于java以及kotlin來說。泛型類型無法被當作真實類型。
是以,kotlin出現了 内斂特化reified
内斂特化reified
當泛型使用reified修飾過之後,發現泛型可以向下傳遞,以及擷取到對應的java類了!
内斂特化的簡單執行個體運用(簡單定義一個Gson的擴充函數吧)
inline fun <reified T> Gson.fromJson(json: String): T = fromJson(json, T::class.java)
fun main() {
val gson = Gson()
//類型自動推導
val person:Person = gson.fromJson("""{...}""")
val am = gson.fromJson<Person>("""{...}""")
}
總結
以上,就是泛型相關了内容了。學習了這麼多,下面寫一個小demo吧。
Demo做一個可靈活配置的通知基類,響應式程式設計(核心思想就是,通過泛型傳遞父類可擷取到子類類型)。
首先我們定義一些通用的頭檔案
typealias OnConfirm = () -> Unit
typealias OnCancel = () -> Unit
private val EmptyFunction = {}
定義一個轉換的接口
interface SelfType<Self> {
val self: Self
get() {
// 将目前的類型轉入傳入的泛型
return this as Self
}
}
寫一個通知類
open class Notification(val title: String, val content: String)
open class ConfirmNotification(
title: String,
content: String,
val onConfirm: OnConfirm,
val onCancel: OnCancel
) : Notification(title, content)
寫一個通知的建構類
open class NotificationBuilder<Self : NotificationBuilder<Self>> : SelfType<Self> {
// protected 隻能私有或者繼承類使用
protected var title = ""
protected var content = ""
fun title(title: String): Self {
this.title = title
return self
}
fun content(content: String): Self {
this.content = content
return self
}
// 應該判斷指派情況 這裡就不判斷了
open fun build() = Notification(this.title, this.content)
}
再寫 ConfirmNotificationBuilder 這時候隻要把 ConfirmNotificationBuilder 類型通過泛型傳遞過去 , NotificationBuilder title以及content傳回的 self類型 就是ConfirmNotificationBuilder 了
class ConfirmNotificationBuilder : NotificationBuilder<ConfirmNotificationBuilder>() {
// 定義為 私有的 不需要繼承
private var onConfirm: OnConfirm = EmptyFunction
private var onCancel: OnCancel = EmptyFunction
fun onConfirm(onConfirm: OnConfirm): ConfirmNotificationBuilder {
this.onConfirm = onConfirm
return this
}
fun onCancel(onCancel: OnCancel): ConfirmNotificationBuilder {
this.onCancel = onCancel
return this
}
// 應該判斷指派情況 這裡就不判斷了
override fun build() = ConfirmNotification(title, content, onConfirm, onCancel)
}
使用
fun main() {
ConfirmNotificationBuilder()
.title("hello")
.content("helloYa")
.onConfirm{
print("confirm")
}.onCancel{
print("cancel")
}
.build()
.onConfirm.invoke()
}