使用处变量

声明一个类型参数T作为out是很方便的,并且如果和子类一起使用将不会有什么问题。当类真正的限制仅仅是返回类型T的值时的确是如此,但是如果不是这样呢?一个恰当的例子就是Array

class Array<T>(val size: Int) {
    fun get(index: Int): T { /* ... */ }
    fun set(index: Int, value: T) { /* ... */ }
}

这个类型参数T不是共变和逆变的。因为其涉及到readwrite的操作。这会导致缺乏弹性。

fun copy(from: Array<Any>, to: Array<Any>) {
    assert(from.size == to.size)
    for (i in from.indices)
        to[i] = from[i]
}

这个函数尝试将数据从Array拷贝到另一个Array,调用代码如下:

val ints: Array<Int> = arrayOf(1, 2, 3)
val any = Array<Any>(3)
copy(ints, any)  // Error: expects (Array<Any>, Array<Any>)

上述的代码调用会出现错误,这里我们又遇到了同样的问题:Array<T>中的T是不可变的,因此Array<Int>Array<Any>并不是相互之间啊的子类。为什么呢?同样的是因为复制可能会引起不好的事情发生。比如其尝试修改值(write),当我们尝试将Int写入String类型的Array时候将会导致 ClassCastException 异常。

因此,唯一需要保证的就是copy()方法不会做出不好的事情。我们想禁止from参数被写入数据。

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>,比如你可以使用CharSequenceObject的数组作为参数调用fill()函数。

results matching ""

    No results matching ""