Kotlin泛型

作者: 漆先生 | 来源:发表于2021-03-30 15:48 被阅读0次

    一、泛型

    Kotlin 中的类也可以有类型参数:

    class Box<T>(t: T) {
        var value = t
    }
    
    val box1: Box<Int> = Box(1)
    
    //类型参数可以推断出来,例如从构造函数的参数或者从其他途径,允许省略类型参数:
    val box2 = Box(1)
    

    二、型变

    声明处型变(declaration-site variance)与类型投影(type projections)。

    1.java通配符

    • 上边界通配限定符,让 Collection<? extends Object>是 Collection<String>的超类
    • 下边界通配限定符,让List<? super String> 是 List<Object> 的⼀个超类

    2.声明处型变

    声明处型变:在类型参数声明处提供。这与 Java 的使用处型变相反。
    out 修饰符:标注 Source 的类型参数 T 来确保它仅从 Source<T> 成员中返回(生产),并从不被消费。

    interface Source<out T> {
        fun nextT(): T
    }
    
    fun demo(strs: Source<String>) {
        val objects: Source<Any> = strs // 这个没问题,因为 T 是⼀个 out-参数
    }
    

    in修饰符:它使得⼀个类型参数逆变:只可以被消费而不可以被生产。逆变类型的⼀个很好的例子是 Comparable :

    interface Comparable<in T> {
        operator fun compareTo(other: T): Int
    }
    
    fun demo(x: Comparable<Number>) {
        x.compareTo(1.0)                // 1.0 拥有类型 Double,它是 Number 的⼦类型
        val y: Comparable<Double> = x   // 因此,我们可以将 x 赋给类型为 Comparable <Double> 的变量 
    }
    

    三、类型投影

    1.使用处型变:类型投影

    val ints: Array<Int> = arrayOf(1, 2, 3) 
    val any = Array<Any>(3) { "" } 
    copy(ints, any)
    
    fun copy(from: Array<out Any>, to: Array<Any>) { …… }
    

    类型投影: from 不仅仅是⼀个数组,而是⼀个受限制的(投影的)数组,只可以调用返回类型为类型参数 T 的方法,如上,这意味着我们只能调用 get() 。这就是我们的使用处型变的用法, 并且是对应于 Java 的 Array<? extends Object> ,但使用更简单些的方式。
    in 投影⼀个类型:

    fun fill(dest: Array<in String>, value: String) { …… }
    

    Array<in String> 对应于 Java 的 Array<? super String> ,也就是说,你可以传递⼀个 CharSequence 数组或⼀个 Object 数组给 fill() 函数。

    2.星投影

    对类型参数一无所知,但仍然希望以安全的方式使用它。定义泛型类型的投影,该泛型类型的每个具体实例化将是该投影的子类型。 Kotlin 星投影语法:

    • 对于 Foo <out T : TUpper> ,其中 T 是⼀个具有上界 TUpper 的协变类型参数,Foo <> 等价于 Foo <out TUpper> 。这意味着当 T 未知时,你可以安全地从 Foo <> 读取 TUpper 的值。
    • 对于 Foo <in T> ,其中 T 是⼀个逆变类型参数,Foo <> 等价于 Foo <in Nothing> 。这意味着当 T 未知时,没有什么可以以安全的⽅式写⼊ Foo <> 。
    • 对于 Foo <T : TUpper> ,其中 T 是⼀个具有上界 TUpper 的不型变类型参数,Foo<*> 对于读取值 时等价于 Foo<out TUpper> ⽽对于写值时等价于 Foo<in Nothing>
      果泛型类型具有多个类型参数,则每个类型参数都可以单独投影:
    • Function<*, String> 表示 Function<in Nothing, String>
    • Function<Int, *> 表示 Function<Int, out Any?>
    • Function<*, *> 表示 Function<in Nothing, out Any?> 。

    四、泛型函数

    不仅类可以有类型参数。函数也可以有。类型参数要放在函数名称之前:

    fun <T> singletonList(item: T): List<T> ?{
        // ……
    }
    
    fun <T> T.basicToString(): String { // 扩展函数
        // ……
    }
    

    五、泛型约束

    能够替换给定类型参数的所有可能类型的集合可以由泛型约束限制。

    1.上界

    约束类型是与 Java 的 extends 关键字对应的 上界,冒号之后指定的类型是上界:只有 Comparable<T> 的⼦类型可以替代 T

    fun <T : Comparable<T>> sort(list: List<T>) { …… }
    

    默认的上界(如果没有声明)是 Any? 。在尖括号中只能指定⼀个上界。如果同⼀类型参数需要多个上界,我们需 要⼀个单独的 where-子句:

    fun <T> copyWhenGreater(
        list: List<T>,
        threshold: T
    ): List<String> where T : CharSequence, T : Comparable<T> {
        return list.filter { it > threshold }.map { it.toString() }
    }
    

    六、类型擦除

    Kotlin 为泛型声明用法执行的类型安全检测仅在编译期进行。运行时泛型类型的实例不保留关于其类型实参的任何信息。其类型信息称为被擦除。例如,Foo<Bar> 与 Foo<Baz?> 的实例都会被擦除为 Foo<*>

    相关文章

      网友评论

        本文标题:Kotlin泛型

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