天天看點

Kotlin學習(六):kotlin泛型一、泛型的基本概念二、泛型限制三、泛型形變四、星投影 Star Projection四、泛型的實作原理與内斂特化總結

系列文章路引 👀 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的擴充函數吧)
  • 總結

一、泛型的基本概念

泛型的概念

  • 泛型是一種類型層面的抽象
  • 泛型通過泛型參數實作構造更加通用的類型能力
  • 泛型可以讓符合繼承關系的類型批量實作某些功能

泛型的基本聲明方式

Kotlin學習(六):kotlin泛型一、泛型的基本概念二、泛型限制三、泛型形變四、星投影 Star Projection四、泛型的實作原理與内斂特化總結

簡單例子:

定義函數,比較任意類型的大小 <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表示逆變

Kotlin學習(六):kotlin泛型一、泛型的基本概念二、泛型限制三、泛型形變四、星投影 Star Projection四、泛型的實作原理與内斂特化總結

不變

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()
    }
}
           
Kotlin學習(六):kotlin泛型一、泛型的基本概念二、泛型限制三、泛型形變四、星投影 Star Projection四、泛型的實作原理與内斂特化總結
  • 子類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
}
           
Kotlin學習(六):kotlin泛型一、泛型的基本概念二、泛型限制三、泛型形變四、星投影 Star Projection四、泛型的實作原理與内斂特化總結
Kotlin學習(六):kotlin泛型一、泛型的基本概念二、泛型限制三、泛型形變四、星投影 Star Projection四、泛型的實作原理與内斂特化總結
Kotlin學習(六):kotlin泛型一、泛型的基本概念二、泛型限制三、泛型形變四、星投影 Star Projection四、泛型的實作原理與内斂特化總結
  • 子類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)
           
Kotlin學習(六):kotlin泛型一、泛型的基本概念二、泛型限制三、泛型形變四、星投影 Star Projection四、泛型的實作原理與内斂特化總結

比較适合作為描述類場景使用:如下

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學習(六):kotlin泛型一、泛型的基本概念二、泛型限制三、泛型形變四、星投影 Star Projection四、泛型的實作原理與内斂特化總結

是以,kotlin出現了 内斂特化reified

内斂特化reified

當泛型使用reified修飾過之後,發現泛型可以向下傳遞,以及擷取到對應的java類了!

Kotlin學習(六):kotlin泛型一、泛型的基本概念二、泛型限制三、泛型形變四、星投影 Star Projection四、泛型的實作原理與内斂特化總結

内斂特化的簡單執行個體運用(簡單定義一個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()
}