美文网首页
15.Kotlin使用处协变的意义与用法

15.Kotlin使用处协变的意义与用法

作者: leofight | 来源:发表于2018-02-25 13:40 被阅读0次

类型投影

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可能做坏事,也就是说,例如它可能尝试写一个 Stringfrom,并且如果我们实际上传递一个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()。这就是我们的使用处型变的用法,并且是对应于 JavaArray<? extends Object>、 但使用更简单些的方式。

也可以使用 in投影一个类型:

fun setValue(to: Array<in String>, index: Int, value: String) {
    to[index] = value
}

Array<in String> 对应于JavaArray<? 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
}

相关文章

  • 15.Kotlin使用处协变的意义与用法

    类型投影 use-site variance(type projection),使用处型变(类型投影) 将类型参数...

  • 14.从底层实现剖析Kotlin协变与逆变的原理

    1.Java与Kotin的协变对比 Kotlin: 声明处协变; Java:使用处协变。Kotlin中的out关键...

  • Kotlin学习笔记 - 泛型

    1. 基本用法 2. 型变 型变包括 协变、逆变、不变 三种: 协变:泛型类型与实参的继承关系相同 逆变:泛型类型...

  • JAVA泛型与类型安全

    1. 基础泛型 2. 协变与逆变与不变 协变 简单来说即: Java中的数组是协变的 逆变与协变相对,逆转了类型关...

  • Kotlin协程使用

    目录 协程的用法 协程同步异步请求与Rxjava,原生写法的区别与优势 对于协程的理解 1.协程的用法 在安卓中添...

  • Kotlin 泛型协变与逆变的理解

    协变与逆变定义 逆变与协变用来描述类型转换后的继承关系 协变:如果 A 是 B 的子类型,并且Generic 也...

  • Java中的逆变与协变

    什么是逆变与协变 协变(Covariance) 如果B是A的子类,并且F(B)也是F(A)的子类,那么F即为协变 ...

  • Java协变和逆变

    泛型的协变与逆变 协变与逆变用来描述类型转换(type transformation)后的继承关系,其定义如下:如...

  • 泛型编程中的型变

    在泛型编程中,经常会提到型变。型变分为两种:协变与逆变。协变covariant表示与泛型参数T的变化相同,而逆变c...

  • 开始使用Kotlin协程

    本文主要介绍协程的用法, 以及使用协程能带来什么好处. 另外, 也会粗略提一下协程的大致原理.本文的意义可能仅仅是...

网友评论

      本文标题:15.Kotlin使用处协变的意义与用法

      本文链接:https://www.haomeiwen.com/subject/mbbizxtx.html