1

I have a method accepting a vararg of the form

fun arrayOfArrays(vararg aoa: Array<Any>) {
}

Now, I have trouble understanding how to call this method, e.g.

fun callArrayOfArrays() {
    arrayOfArrays(arrayOf(1), arrayOf(1))      // 0) works
    val a = arrayOf(1)
    arrayOfArrays(a, a)                        // 1) type mismatch: inferred type Array, but Array was expected
    val aoa = arrayOf(a)
    arrayOfArrays(aoa)                         // 2) type mismatch: inferred type Array<array>, but Array was expected
    arrayOfArrays(*aoa)                        // 3) type mismatch: inferred type Array<array>, but Array<out array> was expected
    arrayOfArrays(aoa.toList().toTypedArray()) // 4) works
}

UPDATE: After getting a heads-up by a colleague, we figured that adding types to arrayOf() fixes some of my problems, i.e. the following does work now:

fun callArrayOfArrays() {
    arrayOfArrays(arrayOf(1), arrayOf(1))
    val a = arrayOf<Any>(1)
    arrayOfArrays(a, a)
    val aoa = arrayOf<Array<Any>>(a)
    arrayOfArrays(*aoa)
    arrayOfArrays(aoa.toList().toTypedArray())
    arrayOfArrays(*(aoa.toList().toTypedArray()))
}

I still believe that the former should be fine, too. And I do yearn for an understandable explanation for this behavior.

I appreciate that case 0 works, but I fail to understand all the other cases.

For case 1, I would expect that assigning the arrayOf(1) to a variable does not change the semantics, but here we are.

For case 2, I would expect that it works like I would expect the first case to work, just the "Any" being an Array here.

For case 3, I can see the difference, but I do not understand it, and certainly do not know how to ever make this work.

For case 4, I believe this is the vararg taking a single array. However, I cannot spread it either.

1 Answer 1

2

I believe this has to do with what T is inferred to be in the calls to arrayOf<T>(...).

The way I understand it is the following:

In case 0, the param type expected by arrayOfArrays() is Array<Any>, and thus it makes the compiler infer Array<Any> as the type of the expression arrayOf(1). The compiler then checks that 1 is an instance of Any, which is true (because Int is a subtype of Any), and therefore 1 is a valid parameter for arrayOf<Any>().

In case 1, though, arrayOf(1) has no other type information than the parameter 1, which is an Int, and so the expression as a whole is inferred to have the type Array<Int>, which is not a subtype of Array<Any> (because of the invariance of the generic type parameter here). The error message is not that great, though, I have to admit.

Same goes for case 2, where the type of aoa is Array<Array<Int>> instead of Array<Any>. Again, the error message is not very helpful.

Case 3 is really the same as case 1, where each element in aoa is expected to be of type Array<Any> but is of type Array<Int>. It could work if Array was covariant in T, that is probably where the "expected Array<out Any>" comes from, but I really don't fully understand the error message here.

Case 4 is similar to case 0 in the sense that the compiler has more local type information (the type expected by arrayOfArrays()), and so it guides the way it will pick the T when interpreting the declaration of toTypedArray().

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for taking the time! This does make sense to me. In my actual code, I only had to insert a few type parameters and all errors were gone. I do not yet understand why vararg T implies Array<out T>, but I will get to that eventually, I hope ;-)
@Matthias I guess you have already given a look, but just in case you want to find out more about these out and in keywords, you can read more about generics and variance in Kotlin here

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.