Kotlin

Kotlin泛型

2021-03-30  本文已影响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通配符

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 星投影语法:

四、泛型函数

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

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<*>

上一篇下一篇

猜你喜欢

热点阅读