天天看點

瘋狂Kotlin講義學習筆記10章:泛型

1、定義泛型接口、泛型類

泛型在定義類,接口,函數時使用泛型形參,這個泛型形參将在聲明變量、建立對象,調用方法時動态地指定(傳入實際的類型,也可稱為類型實參)

可以為任何類、接口增加泛型聲明

//定義App類時使用了泛型聲明
open class Apple<T>{
    //使用泛型T定義屬性
    open var info:T?
    constructor(){
        info=null
    }

    //使用泛型T來定義構造器
    constructor(info:T){
        this.info=info
    }
}


fun main(args:Array<String>){
    //由于傳給泛型T的是String,所有構造器的參數隻能是String
    var a1:Apple<String> = Apple<String>("蘋果")
    //由于傳給泛型T的是Int,所有構造器的參數隻能是Int
    var a2:Apple<Int> = Apple(3)
    //由于傳給泛型T的是Double,系統推斷泛型形參為Double類型
    var a3=Apple(5.67)
    println(a1.info)
    println(a2.info)
    println(a3.info)

}
           

列印:

蘋果

3

5.67

2、從泛型派生子類

當建立了帶泛型聲明的接口、父類之後,可以為該接口建立實作類,或者從該父類派生子類。

當使用這些接口,父類時不能再包含泛型形參。

父類形參為泛型形參,子類應當明确泛型形參的具體類型

//定義App類時使用了泛型聲明
open class Apple<T>{
    //使用泛型T定義屬性
    open var info:T?
    constructor(){
        info=null
    }

    //使用泛型T來定義構造器
    constructor(info:T){
        this.info=info
    }
}

class A1:Apple<String>(){
    //正确重寫了父類的屬性,屬性類型與父類Apple<String>的屬性類型完全相同
    override  var info:String?=null
        get() = "子類"+super.info
}

fun main(args:Array<String>){
    var a1:A1=A1()
    println(a1.info)

}
           

列印

子類null

3、泛型型變的需要(實際就是轉型,便于添加一個類型的集合到另外一個類型的集合裡)

型變實際就是轉型,便于添加一個類型的集合到另外一個類型的集合裡。

java是通過通配符?來完成的。但通配符的上限是Object類

泛型存在如下規律

通配符上限(泛型協變)意味着從中取出(out)對象是安全的,但傳入對象(in)則不可靠

通配符下限(泛型逆變)意味着想起傳入(in)對象是安全的。但取出對象(out)則不可靠

kotlin抛棄了泛型通配符文法,而是利用in,out讓泛型支援型變

4、聲明處型變

kotlin處理型變的規則

  • 如果泛型隻需要出現在方法的傳回值聲明中(不出現在形參聲明中),那麼該方法就隻是取出泛型對象,是以該方法就支援泛型協變(相當于通配符上限);如果一個類的所有方法都支援泛型協變,那麼該類的泛型參數就可以使用out修飾
  • 如果泛型值需要出現在方法的形參聲明中(不出現在傳回值聲明中),那麼該方法就隻是傳入泛型對象,是以該方法就支援泛型逆變(相當于通配符下限);如果一個類的所有方法都支援泛型逆變,那麼該類的泛型參數可以用in修飾

支援協變的案例

class User<out T>{
    //此處不能用var,否則就有setter方法,setter方法會導緻T出現在方法形參中
    val info:T
    constructor(info:T){
        this.info=info
    }
    fun test():T{
        println("執行test方法")
        return  info
    }

}
fun main(args:Array<String>){
    //此時T的類型是String
    var user=User<String>("瘋狂教育體系")
    println(user.info)
    //對于u2而言,她的類型是User<Any>,此時T的類型是Any,由于程式聲明了T支援協變,是以User<String>可當成User<Any>使用
    var u2:User<Any> = user//取出資料,所有将上限傳入小類型是可行的
    println(u2.info)

}
           

列印:

瘋狂教育體系

瘋狂教育體系

支援逆變的案例

class Item<in T>{
    fun foo(t:T){
        println(t)
    }
}
fun main(args:Array<String>){
    //此時T的類型是Any
    var item=Item<Any>()
    item.foo(20)
    var im2:Item<String> =item

    //im2的實際類型是Item<Any>,是以她的foo的參數隻要是Any即可
    //但我們聲明了im2的類型為Item<String>,是以傳入的參數值可能是String,是以程式肯定是安全
    im2.foo("瘋狂kotlin講義")
}
           

列印

20

瘋狂kotlin講義

kotilin處理規則

  • 如果泛型T隻出現在該類的方法的傳回值聲明中,那麼該泛型參數形參即可使用out修飾T
  • 如果泛型T隻出現在該類的方法的形參聲明中,那麼該泛型參數即可使用in修飾T

泛型在參數聲明的時候出現,這是聲明處型變

5、使用處型變:類型投影

聲明處型變導緻此類所有方法都要用泛型聲明傳回值類型

使用處型變就是在使用泛型時對其使用out或in修飾

看不下去了,過吧

6、星号投影

7、泛型函數的使用

8、具體化類型參數

9、設定形參類型的上限

繼續閱讀