为什么Kotlin的Type的vararg被视为Array <out Type>而不是Array <Type>

时间:2019-06-28 08:08:57

标签: generics kotlin variadic-functions

以下方法使用Java进行编译:

public class Main {
    public static void main(String[] args) {
        varargMethod(1, 2.0);
    }

    static void varargMethod(Number... va) {
        arrayMethod(va);
    }

    static void arrayMethod(Number[] arr) {
        for (Number number : arr) {
            System.out.println(number);
        }
    }
}

如果我尝试在Kotlin中编写类似的代码,则会出现类型不匹配错误:

fun main() {
    varargFun(1, 2.0)
}

fun varargFun(vararg va: Number) {
    arrayFun(va) // Error:(6, 14) Kotlin: Type mismatch: inferred type is Array<out Number> but Array<Number> was expected
}

fun arrayFun(arr: Array<Number>) {
    arr.forEach {
        println(it)
    }
}

我期望va的类型为Array<String>,但是它是Array<out String>。如果我将其投射:va as Array<Number>,则会收到警告:

  

警告:(6,21)Kotlin:未经检查的演员表:从阵列到阵列

我应该如何将vararg作为Array传递给另一个函数,而又不会收到警告和错误?

2 个答案:

答案 0 :(得分:5)

区别在于Java数组中是协变的,即以下内容是有效的:

public static void main(String[] args) {
    Number[] numbers = new Number[0];
    Integer[] ints = new Integer[0];

    numbers = ints;
}

但是,数组在Kotlin中不是协变的,即以下给出了编译错误:

var numbers: Array<Number> = arrayOf()
val ints: Array<Int> = arrayOf()

numbers = ints // error: required Array<Number>, found Array<Int>

不过,您可以使用关键字out声明该数组是一个生产者(即,您保证不会在其中插入任何内容;编译器会确保这一点)。这使数组协变,即以下内容有效:

var numbers: Array<out Number> = arrayOf() // we will only extract Numbers out of this array
val ints: Array<Int> = arrayOf()

numbers = ints // this is ok

鉴于,如果未将vararg va: Number视为Array<out Number>,那么您只能使用Number对象而不是其子类来调用方法。也就是说,以下操作将失败:

fun main() {
    varargFun(arrayOf<Int>(1, 2))
}

fun varargFun(va: Array<Number>) {
    arrayFun(va)
}

但是再次使用outvararg就是这样做的),它神奇地起作用了:

fun main() {
    varargFun(arrayOf<Int>(1, 2)) // error: required Array<Number>, found Array<Int>
}

fun varargFun(va: Array<out Number>) {
    arrayFun(va)
}

答案 1 :(得分:1)

This is covered in the Kotlin documentation

  

在函数内部,类型为let escapeString = unescapeString.replace(/([<>*()?])/g, "\\$1") 的{​​{1}}参数作为vararg的数组可见,即,上面示例中的[...]变量的类型为{{1 }}。

解决问题的方法很简单:忽略Kotlin的护栏和copy the arguments

T