Kotlin学习手记--泛型泛型约束泛型型变星投影泛型擦除内联特化

Posted 川峰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin学习手记--泛型泛型约束泛型型变星投影泛型擦除内联特化相关的知识,希望对你有一定的参考价值。





泛型约束:

fun <T : Comparable<T>> maxOf(a: T, b: T): T 
    return if (a > b) a else b


fun main() 
    val max = maxOf("Hello", "World")
    println(max) //输出World


多个泛型参数,可以每个泛型都有约束:

fun <T, R> callMax(a: T, b: T): R
        where T : Comparable<T>, T : () -> R, //T有两个约束
              R : Number  //R有一个约束
    return if (a > b) a() else b()

类似的还有Map类,它的KV泛型都进行了约束:

class Map<K, V> where K : Serializable, V : Comparable<V>

协变:






简而言之就是用out关键字修饰的泛型就是协变,协变有个继承的关系,比如Int是Number的子类,返回值为协变泛型类型的称为协变点。协变点主要是指输出的类型,用来生成某个实例(生产者)。

例子:

interface Book

interface EduBook : Book

class BookStore<out T : Book> 
    fun getBook(): T 
        TODO()
    


fun covariant()
    val eduBookStore: BookStore<EduBook> = BookStore<EduBook>()
    val bookStore: BookStore<Book> = eduBookStore

    val book: Book = bookStore.getBook() //书店只能获取普通的书
    val eduBook : EduBook = eduBookStore.getBook() //教辅书店只能获取教辅书籍

子类获取子类,父类获取父类,能提供子类的自然可以提供父类。

逆变:





简而言之就是用in关键字修饰的泛型就是逆变,作为函数输入参数的泛型称为逆变点。逆变点主要是指输入的参数类型,是消费者。并且消费者的继承关系跟协变是相反的。

例子:

open class Waste

class DryWaste : Waste()

class Dustbin<in T : Waste> 
    fun put(t: T) 
        TODO()
    


fun contravariant()
    val dustbin: Dustbin<Waste> = Dustbin<Waste>()
    val dryWasteDustbin: Dustbin<DryWaste> = dustbin

    val waste = Waste()
    val dryWaste = DryWaste()

    //普通垃圾桶可以放入任何垃圾
    dustbin.put(waste)
    dustbin.put(dryWaste)

    //干垃圾桶只能放入干垃圾,不能放普通垃圾
//    dryWasteDustbin.put(waste)
    dryWasteDustbin.put(dryWaste)


星投影


星投影在所有逆变点的下限类型是Nothing, 因此不能用在属性或函数上



说白了只是一个描述符,可以简写泛型参数而已。

fun main() 
    val queryMap: QueryMap<*, *> = QueryMap<String, Int>()
    queryMap.getKey()
    queryMap.getValue()

    val f: Function<*, *> = Function<Number, Any>()
    //f.invoke()

    if (f is Function) 
        (f as Function<Number, Any>).invoke(1, Any())
    

    maxOf(1, 3)

    HashMap<String, List<*>>()
    //endregion

    val hashMap: HashMap<*, *> = HashMap<String, Int>()
    //hashMap.get()



class QueryMap<out K : CharSequence, out V : Any> 
    fun getKey(): K = TODO()
    fun getValue(): V = TODO()


fun <T : Comparable<T>> maxOf(a: T, b: T): T 
    return if (a > b) a else b


class Function<in P1, in P2> 
    fun invoke(p1: P1, p2: P2) = Unit


泛型擦除(伪泛型)


Java与Kotlin实现机制一样,在运行时擦除真正的类型,C#则会真的生成一个类型去执行。

内联特化:


内联特化在调用的地方会替换到调用处,因此这时类型是确定的了,即已经特化成某个具体类型。通过fun前面的关键字 inline 和泛型参数T前面的 reified 参数两个来指定泛型参数在调用处实例化。

inline fun <reified T> genericMethod(t: T)
    //val t = T()
    val ts = Array<T>(3)  TODO() 
    val jclass = T::class.java
    val list = ArrayList<T>()
    if(list is List<*>)
        println(list.joinToString())
    


class Person(val age: Int, val name: String)

inline fun <reified T> Gson.fromJson(json: String): T = fromJson(json, T::class.java)

fun main() 
    val gson = Gson()
    val person2: Person = gson.fromJson(""""age":18,"name":"Bennyhuo"""")
    val person3 = gson.fromJson<Person>(""""age":18,"name":"Bennyhuo"""")

实例:模仿的Self Type

typealias OnConfirm = () -> Unit
typealias OnCancel = () -> Unit

private val EmptyFunction = 

open class Notification(
    val title: String,
    val content: String
)

class ConfirmNotification(
    title: String,
    content: String,
    val onConfirm: OnConfirm,
    val onCancel: OnCancel
) : Notification(title, content)

interface SelfType<Self> 
    val self: Self
        get() = this as Self //当前类型强转成Self类型


//泛型添加约束只能传子类
open class NotificationBuilder<Self: NotificationBuilder<Self>>: SelfType<Self> 
    protected var title: String = ""
    protected var content: String = ""

    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)


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")
        .onCancel 
            println("onCancel")
        .content("World")
        .onConfirm 
            println("onConfirmed")
        
        .build()
        .onConfirm()

如果不定义SelfType类型,则子类在调用ConfirmNotificationBuilder().title(“Hello”)之后不能再继续调用子类的onCancel 方法,因为返回的是父类型,但是实际运行时这个类型是子类型。

实例: 基于泛型实现 Model 实例的注入

import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KProperty

abstract class AbsModel 
    init 
        Models.run  this@AbsModel.register() 
    


class DatabaseModel : AbsModel() 
    fun query(sql: String): Int = 0


class NetworkModel : AbsModel() 
    fun get(url: String): String = """"code": 0"""


class SpModel : AbsModel() 
    init 
        Models.run  register("SpModel2") 
    

    fun hello() = println("HelloWorld")


object Models 
    private val modelMap = ConcurrentHashMap<String, AbsModel>()

    fun AbsModel.register(name: String = this.javaClass.simpleName) 
        modelMap[name] = this
    

    //String扩展函数
    fun <T: AbsModel> String.get(): T 
        return modelMap[this] as T
    


fun initModels() 
    DatabaseModel()
    NetworkModel()
    SpModel()


//object单例
object ModelDelegate 
    operator fun <T: AbsModel> getValue(thisRef: Any, property: KProperty<*>): T 
        return Models.run 
            property.name.capitalize().get() //利用了String的扩展函数
        
    


class MainViewModel 
    //利用属性代理by by左边指定类型推导泛型
    val databaseModel: DatabaseModel by ModelDelegate
    val networkModel: NetworkModel by ModelDelegate
    val spModel: SpModel by ModelDelegate
    val spModel2: SpModel by ModelDelegate
    // val compileKotlin: KotlinCompile by tasks //gradle这种写法类似,tasks也是属性代理
    // val compileTestKotlin: KotlinCompile by tasks


fun main() 
    initModels()
    val mainViewModel = MainViewModel()
    mainViewModel.databaseModel.query("select * from mysql.user").let(::println)
    mainViewModel.networkModel.get("https://www.imooc.com").let(::println)
    mainViewModel.spModel.hello()
    mainViewModel.spModel2.hello()

以上是关于Kotlin学习手记--泛型泛型约束泛型型变星投影泛型擦除内联特化的主要内容,如果未能解决你的问题,请参考以下文章

C#高级语法之泛型泛型约束,类型安全逆变和协变(思想原理)

Kotlin 五 泛型 枚举

Kotlin学习手记——反射

Kotlin泛型 ① ( 泛型类 | 泛型参数 | 泛型函数 | 多泛型参数 | 泛型类型约束 )

Kotlin泛型的型变之路

Kotlin泛型的型变之路