Why Array.copyOf() is not covariant?

Is there any reason copyOf() was declared as:

inline fun <T> Array<T>.copyOf(): Array<T>

And not as:

inline fun <T> Array<out T>.copyOf(): Array<T>

?

It would make possible to “cast” arrays like this:

val animals = dogs.copyOf<Animal>()

It would be also useful if we need to modify the data provided as vararg param:

fun foo(vararg strings: String) {
    val strings2 = strings.copyOf()
    strings2[0] = "foo"
}

I believe in this case it is safe to perform an unchecked cast, but it is much worse than above:

fun foo(vararg strings: String) {
    @Suppress("UNCHECKED_CAST")
    val strings2 = strings.copyOf() as Array<String>
    strings2[0] = "foo"
}

The implementation could be as simple as:

inline fun <reified T> Array<out T>.copyOf(): Array<T> {
    return Array(size) { this[it] }
}

Is reified a problem for multiplatform code? Or maybe this is to allow more efficient implementations that utilize direct memory copy?

2 Likes

Marking T reified would make it impossible to use copyOf in places where T is not known at compile time.

4 Likes

Ahh, that makes sense, thank you.

We thought about overloading copyOf function with a reified variant, so that it could be used both in reified and non-reified contexts, but unfortunately the current overloading rules in Kotlin don’t permit that.

2 Likes

What about another name like: copyOfTyped(), typedCopyOf() or toTypedArray()?

toTypedArray() may sound odd for copying, but it is consistent with Collection.toTypedArray() which does almost the same thing; and with functions like toList(), toMutableList(), toSet() that are very often used not for converting, but for copying. It would be very inconsistent with existing copyOf() though.

Anyway, it is very easy to implement when needed, so this is not that important.

Also note that in reality arrays aren’t quite covariant (although in Java they are at compile time, even if not at runtime - it is a dog’s breakfast due to starting out without generics).

Yes, I understand arrays are invariant by default. But that does mean they can’t be covariant if we only read from them. And when we copy the array, we only read from it.