kotlin 使用in out限制对集合类只能使用读方法和写方法

结合上一篇java的范型通配符上下届的文章,这里给出kotlin的解决方案。

in, out不仅可以用在定一个使用范型的类时,指定该范型类型只能用在方法的返回值或者输入参数,比如像下边这样:

interface Source<out T> {
    fun nextT(): T
}

fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs // This is OK, since T is an out-parameter
    // ...
}

 这就是变向告诉编译器,demo传入的参数Source<String>,该对象对应的所有使用到范型的方法,都只用在方法的返回值上,并且是String类型的。这样在demo函数内部,就可以放心将strs赋值给Souce<Any>变量,因为该变量获取到对象引用后,无论调用对象中什么方法,如果涉及到范型,一定是返回String的,而返回的String值用Any来接,是没有问题的。

但是in, out不仅仅这一种用法, 下边是kotlin官方文档提到的In, out另外一种用法, 就是不是在生命范型类时使用,而是在定义范型函数时使用,其目的也是告诉编译器,去做一些检查防止代码编写人员作出误操作。比如下边的例子,

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

就是告诉编译器,Array<out Any>不是一个普通的数组,而是一个收到限制的数组(编译器施加的限制而非数据结构本身),他只能调用数组中这样的范型函数 - 其返回值是Any类型的。而对于Array中,接受范型作为参数的函数,则不能使用,这样当你往copy函数中传一个Array<String>类型的对象时,就可以放心传送,不用担心在copy函数内部尝试往from对象数组中写入Any类型的元素。

Type projections

Use-site variance: type projections

It is very easy to declare a type parameter T as out and avoid trouble with subtyping on the use site, but some classes can't actually be restricted to only return T's! A good example of this is Array:

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

Copied!

This class can be neither co- nor contravariant in T. And this imposes certain inflexibilities. Consider the following function:

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

Copied!

This function is supposed to copy items from one array to another. Let's try to apply it in practice:

val ints: Array<Int> = arrayOf(1, 2, 3)
val any = Array<Any>(3) { "" }
copy(ints, any)
//   ^ type is Array<Int> but Array<Any> was expected

Copied!

Here you run into the same familiar problem: Array<T> is invariant in T, and so neither Array<Int> nor Array<Any> is a subtype of the other. Why not? Again, this is because copy could have an unexpected behavior, for example, it may attempt to write a String to from, and if you actually pass an array of Int there, a ClassCastException will be thrown later.

To prohibit the copy function from writing to from, you can do the following:

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

This is type projection, which means that from is not a simple array, but is rather a restricted (projected) one. You can only call methods that return the type parameter T, which in this case means that you can only call get(). This is our approach to use-site variance, and it corresponds to Java's Array<? extends Object> while being slightly simpler.

You can project a type with in as well:

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

Array<in String> corresponds to Java's Array<? super String>. This means that you can pass an array of CharSequence or an array of Object to the fill() function.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值