Kotlin系列五:泛型及其高級特性

目錄

一 泛型

1.1 泛型類

1.2 泛型方法

 1.3 限制泛型類型

二 類委託和委託屬性

2.1 類委託

2.2 委託屬性

三 泛型的高級特性

3.1 泛型實化

3.2 泛型的協變

3.3 泛型的逆變


一 泛型

1.1 泛型類

class MyClass <T>{
    fun method(param :T): T {
        return param
    }
}
val myClass =MyClass<Int> ()
val result = myClass.method(123)

1.2 泛型方法

class MyClass {
    fun <T> method(param :T): T {
        return param
    }
val myClass =MyClass ()
val result = myClass.method<Int>(123)

//Kotlin出色的類型推導機制
val myClass =MyClass ()
val result = myClass.method(123)

實踐舉例:完成一個類似apply擴展函數的build功能

fun <T> T.build(block:T.() ->Unit):T{
        block
        return this
    }

 1.3 限制泛型類型

舉例:將泛型上界設置爲Number 類型(將method方法的泛型指定成數字類型)

class MyClass {
    fun <T :Number> method(param :T): T {
        return param
    }
}

默認情況下,所有泛型都是可以指定成可空類型的,這是因爲在不動手指定上界的時候,泛型的默認上界是Any?;如果想讓泛型的類型不可爲空,只需將上界指定成Any 。


二 類委託和委託屬性

委託是一種設計模式 ,它的基本理念是 :操作對象自己不會去處理某段邏輯,而是把工作委託給另外一個輔助對象去處理。

好處: 如果一個類,讓大部分的方法實現都是調用輔助對象中的方法,而少部分的方法是自己實現的。這就是委託模式的意義所在。

2.1 類委託

類委託核心思想:將一個類的具體實現委託給另一個類去完成。藉助於委託模式,我們可以輕鬆實現一個自己的實現類。如:定義一個MySet,並讓他實現Set接口:

class MySet<T>(val helperSet:HashSet<T>):Set<T>{
    override val size: Int
        get() = helperSet.size
    override fun contains(element: T): Boolean = helperSet.contains(element)
 
    override fun containsAll(elements: Collection<T>): Boolean  = helperSet.containsAll(elements)
 
    override fun isEmpty(): Boolean = helperSet.isEmpty()
 
    override fun iterator(): Iterator<T> = helperSet.iterator()
 
}
 
//下面代碼相當於上面代碼,將操作委託給helperSet
class MySet<T>(val helperSet:HashSet<T>):Set<T> by helperSet{
 
}

這其中接收的一個HashSet參數,這就相當於一個輔助對象;然後在Set接口中的所有的方法的實現中,我們都沒有進行自己的實現,而是調用了輔助對象中的相應的方法實現。這就是一種委託模式。

但是如果有成百上千的返方法都要如上代碼中那樣去調用輔助對象中的相應代碼實現,就要哭了。 爲了避免這種調用對象中的待實現方法過多的情況,使用的關鍵字 by加上受委託的輔助對象進行類委託,這樣可以省卻很多的模板代碼編寫。

//MySet中有所有Set接口中的功能,和HashSet保持一致, 並且isEmpty是自己實現的
class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet {

    fun hello() = println("hello")

    //如果你想重寫set的某個方法,還能自己重寫 ,不想就委託HashSet的用它的
    override fun isEmpty(): Boolean {
        return true
    }

}

2.2 委託屬性

委託屬性 將一個屬性(字段)的具體實現委託給另外一個類去完成委託屬性的語法結構:

class MyClass{
    var p by Delegate()
}

lazy函數懶加載技術原理:通過by委託屬性然後在調用獲取值的時候才調用getValue


三 泛型的高級特性

3.1 泛型實化

java和kotlin都是類型擦除機制,泛型只是對於編譯器的類型的約束,運行期是識別不出來我們代碼中指定的泛型類型的,所以肯定實現不了 a is T 或者 T::class.java 這樣的用法。

但是Koltlin可以利用內聯函數和reified關鍵字將內聯函數中的泛型進行實化。例如:inline fun <reified T> getGenricType() = T::class.java

泛型實化的應用:

//用到了內聯函數實化,擴展函數,高階函數
inline fun <reified T> startActivity(context:Context,block:Intent.() -> Unit){
    val intent = Intent(context,T::class.java)
    intent.block()
    context.startActivity()
}
 
//用到了lambda表達式
startActivity<TestActivity>(this){
    putExtra("param1","data")
    putExtra("param2",123)
}

面試問題:爲什麼Kotlin可以泛型實例化?

泛型實例化要求必須是inline修飾的內聯函數中的泛型,而內聯函數在進行代碼替換後,替換後的代碼中是可以獲得泛型的實際類型的(因爲你泛型T本就是寫在了內聯函數fun <T> xxx 中的T中)。這時你還用refield關鍵字來表示要對該泛型進行實例化,當然就可以獲得該泛型具體的類型並實例化了。

3.2 泛型的協變

java中不允許List<Student>是List<Person>的子類,原因如下:

class SimpleData<T> {
    
    private var data:T? = null
 
    fun set(t:T?){
        data = T
    }
 
    fun get():T = data
}
 
val student = Student()
val data = SimpleData<Student>()
data.set(student)
val teacher = Teacher()
data.set(teacher)
val studentData = data.get()//此處就存在隱患,不知道是Student還是Teacher

協變:假如A是B的子類型,則MyClass<A>也是MyClass<B>的子類型

實現如下:利用out關鍵字

class SimpleData<out T>(val data:T) {
 
    fun get():T = data
 
}

3.3 泛型的逆變

如果A是B的子類型,則MyClass<B>是MyClass<A>的子類型

interface Transformer<T> {
    fun transform(t:T):String
}
 
fun main(){
    val trans = object : Transtormer<Person> {
        override fun transform(t:Person):String = "${t.name}${t.age}"
    }
    handleTransformer(trans)//會報錯,因爲Transtormer<Person>不是Transtormer<Student>子類
}
 
fun handleTransformer(t:Transtormer<Student>){
    val student = Student()
    val result = t.transform(student)
}
 
//逆變修改如下
interface Transformer<in T> {
    fun transform(): @UnsafeVariance T
}
 
fun main(){
    val trans = object : Transtormer<Person> {
        override Teacher()
    }
    handleTransformer(trans)//通過編譯,但是寫法不對
}
 
fun handleTransformer(t:Transtormer<Student>){
    val student = Student()
    val result = t.transform(student)
}
 

具體源碼例子,Comparable用於對比兩個對象大小的接口

interface Comparable<in T>{
    operator fun comparaTo(other:T):Int
}

 

 

注:本文主要例子和參考

郭霖《第一行代碼》 Kotlin部分

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章