美文网首页
Kotlin-泛型

Kotlin-泛型

作者: 风雨渡江 | 来源:发表于2018-05-10 23:28 被阅读11次

    关于什么是泛型, 看另一篇文章 https://www.jianshu.com/p/7eac2f36036e

    参数化函数(泛型函数)

    1. 在函数名前边加上<T>实现参数化函数
    2. 函数的参数至少有一个是T类型, 否则编译器无法推断出T的类型
    fun <T> foo (input: T): T {
        return input
    }
    

    函数的返回值可以是任何类型, 但不能用运行时的类型

    fun <T> foo (input: T): String {
        return input // 即使我们在运行时传入了一个字符串也不行, input的类型是T, 与运行时的类型无关
    }
    

    参数化类型

    参数化类型主要用在各种容器类型上, 这种类型内部可以包含其他类型的数据, 比如list, map

    class Sequence<T> // 定义一个参数化类型
    val seq = Sequence<Int>() //传入Int类型
    

    上界类型约束

    将类型限制为某个类的子类, 如果省略, 将会是Any

    fun <T: Number> convert(a: T, b: T)  // 将T的类型限制为Number的子类
    fun <T> convert(a: T, b: T)  // T的类型限制为Any
    

    Invariance (不变)

    类型不变指泛型类型默认是没有继承上的关系的, M<Int>并不是M<Number>的一个子类型.
    这样设计的原因, 参考这个例子:

    fun foo(m: M<Number>): Unit {
      m.add(123L)
    }
    

    假设有一个M<Int>型的集合x, 执行foo(x)后, 如果类型有继承关系, 由于IntLong都是Number的子类型, 就会往x里添加一个Long型元素, 这违背了类型安全的原则.

    Covariance (协变)

    协变是改变类型之间的关系, 使他们有继承性.

    fun foo(m: M<Number>): Unit {
      m.functionFromNumber()
    }
    

    假设foo函数会调用一个Number类的方法, 这样我们就不用管传入的是m<int>, 还是m<Long>都无所谓(因为函数定义在父类上), 此时就需要协变`来让类型有继承关系.

    class M<out T>
    

    使用outT定义为协变类型之后, 不能用T作为函数的输入参数(形参), 可以做返回值. 像m.add(T)是非法的.

    逆变 Contravariance

    逆变是反转两个类的继承关系, 比如逆变后, M<Int> 是 M<Number> 的父类.

    这个需求是这样的

    Event<String>(stringHandler)
    Event<Number>(numberHandler)
    

    我们有两种Event, 分别用对应类型的Handler处理, 假设我们想用一个通用的handler比如: commonHandler<Any>`来处理.

    Event<String>(commonHandler)
    Event<Number>(commonHandler)
    

    答案是不行, 因为AnyString的父类, 而Event只能使用String或它的子类. 通过逆变, Any就变成了String的子类.

    class Event<in T>(val handler: Handler<T>)
    class Handler<in T>
    ...
    

    逆变后, 类型只能作为输入参数(形参), 不能作为返回值类型.

    type projection

    类型的variance有两种, use site(java) 和 declaration site(Kotlin), type projection 允许我们使用use site variance, 它的思想是, 如果我们无法在类定义(比如使用别人的类)时使用covariance或contravriance(即declaration site), 我们可以在定义函数时, 规定函数将如何使用类型.

    fun foo(m: M<out Number>): Unit {
      m.functionFromNumber()
    }
    

    同理, contravariant

    class Event<in T>(val handler: Handler<in T>) // 构造函数的in T
    
    type projection定义了我们如何使用函数, 而不需修改原类型声明
    

    Type reification

    由于JVM在编译时会把所有泛型类型(不包括基础数据类型)信息擦除, kotlin同样如此, 使用Type reification, kotlin可以为inline 函数保留泛型类型信息.

    inline fun <reified T>printT(any: Any): Unit {
        if (any is T)
        println("I am a tee: $any")
    }
    

    这个函数可以在运行时获取到T的传入类型. 类型具体化只能用于inline函数, 因为inline函数的执行是把函数体内容直接拷贝到调用处, 此时通过传递的参数可以知道T的类型. 其他函数无法使用类型具体化

    相关文章

      网友评论

          本文标题:Kotlin-泛型

          本文链接:https://www.haomeiwen.com/subject/ncnzrftx.html