09-类型投影
8.2.4 类型投影
大多数情况下,将类型参数T声明为out是非常方便的,而且能避免使用处子类型化带来的麻烦。但在有些情况下,不能限制为只返回T,Array就是一个很好的例子。Array类的代码如下。
class Array<T>(val size: Int) {
fun get(index: Int): T { /* …… */ }
fun set(index: Int, value: T) { /* …… */ }
}
在上面的Array类中,T既是get方法的返回类型,也是set方法的第二个参数。也就是说,T既是生产者,也是消费者,这就导致它无法进行子类化。考虑下面的函数。
fun copy(from: Array<Any> , to: Array<Any>){
assert(from.size == to.size)
for(i in from.indices){
to[i] = from[i]
}
}
上面的函数的作用是复制数组中的数据到另一个数组中,下面将该函数用在实际中。
val ints: Array<Int> = arrayOf(1, 2, 3)
val any = Array<Any>(3){ ""}
copy(ints , any) //编译错误,expects(Array<Any>, Array<Any>)
上面的代码编译是不通过的,因为Array\
因此,为了避免这种不安全的操作,可以使用类型投影(type projection)来加以限制,使用类型投影限制的代码如下。
fun copy(from: Array<out Any> , to: Array<Any>){
//…
}
使用类型投影后,from不再是一个简单的数组,而是一个受限(投影)类型:只能够调用那些返回类型为T的方法,在本例中则只能调用get()。这也是Kotlin使用型变的目的之一,它的作用和Java的Array<? extends Object>相同,但更加简单。当然,也可以使用关键字in来定义一个投影类型。
fun fill(dest: Array<in String> , value: String){
//…
}
代码中,Array\