Почему vararg Kotlin's Type рассматривается как Array <out Type>, а не Array <Type> - PullRequest
4 голосов
/ 28 июня 2019

Следующий метод компилируется в 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: непроверенный актер: Array to Array

Как я могу передать vararg как Array другой функции без предупреждения и ошибок?

Ответы [ 2 ]

5 голосов
/ 28 июня 2019

Разница в том, что в 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)
}

Но опять же, с out (что и делает vararg), оно волшебным образом работает:

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

fun varargFun(va: Array<out Number>) {
    arrayFun(va)
}
1 голос
/ 28 июня 2019

Это описано в документации Kotlin :

Внутри функции vararg -параметр типа T виден как массив T, т. Е. Переменная [...] в приведенном выше примере имеет тип Array<out T>.

Решение вашей проблемы простое: игнорируйте поручни Котлина и скопируйте аргументы .

fun varargFun(vararg va: Number) {
    val copy = arrayOf(*va)
    arrayFun(copy)
}
...