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?
We can use the library function arrayOf() to create an array by passing the values of the elements to the function. Since Array is a class in Kotlin, we can also use the Array constructor to create an array. The constructor takes two parameters: The size of the array, and.
In Kotlin, You can pass a variable number of arguments to a function by declaring the function with a vararg parameter. a vararg parameter of type T is internally represented as an array of type T ( Array<T> ) inside the function body.
4. Spread Operator. Sometimes we have an existing array instance in Kotlin, and we want to pass it to a function accepting a vararg. In those situations, to decompose the array to a vararg, we can use the spread operator: val numbers = intArrayOf(1, 2, 3, 4) val summation = sum(*numbers) assertEquals(10, summation)
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)
}
This is covered in the Kotlin documentation:
Inside a function a
vararg
-parameter of typeT
is visible as an array ofT
, i.e. the [...] variable in the example above has typeArray<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)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With