Pass array of non-nullable strings as array of nullable strings

╄→гoц情女王★ 提交于 2019-12-04 19:33:14


I have a function that takes in an Array<String?>:

fun doStuff(words: Array<String?>) {
    // ...

Is there a way I can pass in a Array<String> to this function? As-is the compiler is giving me a "type mismatch" error.

private val SOME_WORDS = arrayOf("I", "want", "to", "use", "these")

doStuff(SOME_WORDS) // throws a type-mismatch error

Preferably I'd like to avoid making SOME_WORDS an arrayOf<String?>(...) if possible.


Use an out-projected Array<out String?>:

fun doStuff(words: Array<out String?>) { /* ... */ }

Arrays in Kotlin are invariant, meaning that Array<A> and Array<B> are not subtypes of each other for any different A and B, including String and String?, and they cannot be assigned and passed as arguments in place of each other.

Using an out-projection Array<out String?> makes the function accept not only Array<String?> but also arrays with subtypes of String?. Basically, since the type is final, there's only one such subtype, and it is String (the non-null one, without ?).

The picture is taken from: A Whirlwind Tour of the Kotlin Type Hierarchy)

So, the function will accept Arrray<String> as well. But you won't be able to put nullable values into the array (that's how the projection works), so the type safety (and null-safety) are preserved.


The problem is variance here. String is a subtype of its nullable friend String? but in Kotlin this doen't mean Array<String> is a subtype of Array<String?> because arrays are invariant. (In Java this is not the case, arrays are covariant by default)

If you have a function like yours which expects an argument of type Array<String?> you can only pass Array<String?>, thus Array<String> isn't possible! If you want to enable your method to also handle these arrays do this:

fun doStuff(words: Array<out String?>) {
    // ...

This makes the array words covariant and subtypes of Array<String?> are allowed to be passed.

Be aware, that only functions like get() are accessible for such "out-projected" arrays. The Parameter words is said to be a Producer of String?.

If you want to learn more about it, have a look at the official docs :)


You should use the out type projection since the generic type Array<String> extends Any rather than Array<String?> in Kotlin, for example:

//                        v--- use out type projection here
fun doStuff(words: Array<out String?>) {
   // ...

When use the out type projection in Kotlin is like as upper bounded wildcard ? extends T in Java. and the String is a subset of the String? in Kotlin, so the Array<String> extends Array<String?> in Kotlin. As you can see the left side of inheritance diagram is the upper bounded wildcard inheritance.