欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

kotlin中的泛型

程序员文章站 2022-03-12 22:35:50
...

kotlin中的泛型

基本使用

class Box<T>(var t: T)

fun main() {
    var box = Box(1)
    println(box.t) // 1
}

声明处协变

kotlin中的out关键字叫做variance annotation,因为是在类型声明处定义的,所以称为声明处协变。

对于java来说是使用处协变use-site variance,其中类型通配符使得协变成为可能。

如果泛型类只是将泛型类型作为方法的输出值,也就是生产者,那么我们可以使用out,produce=output=out。

interface Producer<out T> {
    fun produce() : T
}

如果泛型类只是将泛型类型作为方法的输入值,也就是消费者,那么我们可以使用in,consume=input=in。

interface Consumer<in T> {
    fun consume(t: T)
}

如果泛型类同时将泛型类型作为方法的输入值和输出值,那么我们不能使用out和in关键字修饰泛型。

interface ProducerConsumer<T> {
    fun produce() : T

    fun consume(t: T)
}

使用处协变

假设现在需要将一个数组中的内容拷贝到另一个数组,浅拷贝,代码如下:

fun copy(from: Array<Any>, to: Array<Any>) {
    assertEquals(from.size, to.size)

    for (i in from.indices) {
        to[i] = from[i]
    }
}

然后调用这个方法:

fun main() {
    var from: Array<Int> = arrayOf(1, 2, 3, 4)
    var to: Array<Any> = Array(4) { "hello$it" }
    copy(from, to) // Type mismatch
}

原因是Array<Int>并不是Array<Any>的子类,假设copy(from, to)这一行编译通过,那么在copy方法内部就可以往from中添加String类型的元素,那么在main方法中从from取出Int类型的元素,就会抛出ClassCastException异常,代码应修改如下:

fun copy(from: Array<out Any>, to: Array<Any>) {...}

kotlin中同样支持in关键字用于使用处逆变,看下面的例子,设置Array某个位置的元素:

fun fill(dest: Array<String>, index: Int, value: String) {
    dest[index] = value
}

使用如下:

var dest2: Array<Any> = Array(4) { "hello" }
fill(dest2, 2, "world") // Type mismatch
for(i in dest2.indices) {
    println(dest2[i])
}

修改如下:

fun fill(dest: Array<in String>, index: Int, value: String) {...}

星投影

Star:如果T的上界是TUpper,那么Star<*>就相当于Star<out TUpper>,这表示当T的类型为未知时,可以从Star<*>中安全的取出TUpper类型的值。

class Star<out T>

fun main() {
    var star = Star<Number>()
    var star2: Star<*> = star
}

Star:如果T的上界是TUpper,那么Star<*>就相当于Star<in Nothing>,这表示你无法向其中写入数据。

class Star2<in T> {
    fun setValue(t: T) {
    }
}

fun main() {
    var star3 = Star2<Number>()
    var star4: Star2<*> = star3
    // star4.setValue(3) // compile error
}

Star:如果T的上界是TUpper,那么Star<*>就相当于读取时的Star<out TUpper>以及写入时的Star<in Nothing>

class Star3<T>(var t: T) {
    fun setValue(t: T) {
    }

    fun getValue(): T = this.t
}

fun main() {
    var star5 = Star3<Number>(6)
    var star6: Star3<*> = star5
    star6.getValue()
    // star6.setValue(2) // compile error
}

泛型函数

泛型除了可以用于类外,还可以用于函数。

fun <T> getValue(t: T) : T  = t

fun main() {
    println(getValue("hello"))
    println(getValue(1024))
}

泛型的约束

约束泛型的父类为Comparable:

class UpperBounds<T: Comparable<T>>

fun main() {
    var upperBounds = UpperBounds<String>()
}

约束泛型的父类为多个:

class UpperBounds2<T> where T: Comparable<T>, T: Number

泛型擦除

泛型其实就是一个语法糖,在编译之后会变成Any类型。

class MyStorage<out T>(private var t: T) {
    fun getValue(): T = t

    fun setValue(t: @UnsafeVariance T) {
        this.t = t
    }
}


fun main() {
    var myStorage = MyStorage(50)
    var myStorage2: MyStorage<Any> = myStorage

    myStorage2.setValue("hello") // 泛型擦除
    println(myStorage2.getValue())
}