类型投影
use-site variance(type projection),使用处型变(类型投影)
将类型参数 T
声明为out
非常方便,并且能避免使用处子类型化的麻烦,但是有些类实际上不能限制为只返回T
!
一个很好的例子是 Array
:
class Array<T>(val size: Int) {
fun get(index: Int): T { ///* …… */ }
fun set(index: Int, value: T) { ///* …… */ }
}
该类在 T
上既不能是协变的也不能是逆变的。这造成了一些不灵活性。考虑下述函数:
fun copy(from: Array<Any>, to: Array<Any>) {
assert(from.size == to.size)
for (i in from.indices)
to[i] = from[i]
}
这个函数应该将项目从一个数组复制到另一个数组。让我们尝试在实践中应用它:
val from: Array<Int> = arrayOf(1,2,3,4)
val to:Array<Any> = Array<Any>(4,{it -> "hello"+it})
copy(from,to)// 错误:期望 (Array<Any>, Array<Any>)
这里我们遇到的问题:Array <T>
在 T
上是不型变的,因此 Array <Int>
和Array <Any>
都不是另一个的子类型。为什么? 再次重复,因为 copy
可能做坏事,也就是说,例如它可能尝试写一个 String
到from
,并且如果我们实际上传递一个Int
的数组,一段时间后将会抛出一个 ClassCastException
异常。
那么,我们唯一要确保的是 copy()
不会做任何坏事。我们想阻止它写到from
,我们可以:
fun copy(from: Array<out Any>, to: Array<Any>) {
assertTrue(from.size == to.size)
for (i in from.indices) {
to[i] = from[i]
}
}
这里发生的事情称为类型投影:我们说from
不仅仅是一个数组,而是一个受限制的(投影的)数组:我们只可以调用返回类型为类型参数 T
的方法,如上,这意味着我们只能调用 get()
。这就是我们的使用处型变的用法,并且是对应于 Java
的Array<? extends Object>
、 但使用更简单些的方式。
也可以使用 in
投影一个类型:
fun setValue(to: Array<in String>, index: Int, value: String) {
to[index] = value
}
Array<in String>
对应于Java
的 Array<? super String>
,也就是说,你可以传递一个CharSequence
数组或一个Object
数组给 setValue() 函数。
完整的示例代码
package com.leofight.kotlin2
import kotlin.test.assertTrue
// use-site variance(type projection),类型投影
//数组的浅拷贝
fun copy(from: Array<out Any>, to: Array<Any>) {
assertTrue(from.size == to.size)
for (i in from.indices) {
to[i] = from[i]
}
}
fun main(args: Array<String>) {
val from: Array<Int> = arrayOf(1, 2, 3, 4)
val to: Array<Any> = Array<Any>(4, { it -> "hello" + it })
for (item in to) {
println(item)
}
copy(from, to)
println("*************")
val array: Array<String> = Array<String>(4, { _ -> "hello" })
for (item in array) {
println(item)
}
println("*************")
setValue(array, 1, "world")
for (item in array) {
println(item)
}
println("*************")
val array2: Array<Any> = Array<Any>(4, { it -> "hello " + it })
for (item in array2) {
println(item)
}
setValue(array2, 1, "world")
println("*************")
for (item in array2) {
println(item)
}
}
fun setValue(to: Array<in String>, index: Int, value: String) {
to[index] = value
}
网友评论