15

The following method gets compiled in 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);
        }
    }
}

If I try to write similar code in Kotlin i get type mismatch error:

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)
    }
}

I expected va to be of type Array<String>, but it is Array<out String>. If I cast it: va as Array<Number>, I get a warning:

Warning:(6, 21) Kotlin: Unchecked cast: Array to Array

How am I supposed to pass vararg as an Array to another function without getting warning and errors?

1
  • You could declare your array function as fun arrayFun(arr: Array<out Number>). Commented Jun 28, 2019 at 8:12

2 Answers 2

14

The difference is that in Java arrays are covariant, i.e. the following is valid:

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

    numbers = ints;
}

However, arrays are not covariant in Kotlin, i.e. the following gives a compilation error:

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

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

However you can declare the array is a producer (i.e. you promise you'll never insert anything inside it; the compiler will make sure of that) with the keyword out. That makes the array covariant, i.e. the following is valid:

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

Given that, if vararg va: Number was not treated as a Array<out Number>, then you could have called your method only with Number objects and not with its subclasses. I.e., the following would fail:

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

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

But again, with an out (which is what vararg does), it magically works:

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

fun varargFun(va: Array<out Number>) {
    arrayFun(va)
}
Sign up to request clarification or add additional context in comments.

Comments

5

This is covered in the Kotlin documentation:

Inside a function a vararg-parameter of type T is visible as an array of T, i.e. the [...] variable in the example above has type Array<out T>.

The solution to your problem is simple: ignore Kotlin's guard rails, and copy the arguments.

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

1 Comment

Isn't it a waste though? The array is already created for you, and you create a whole new one and need to copy all content into it. Why can't you use it directly, like on Java?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.