10

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.

3 Answers 3

8

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 ?).

type variance diagram

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.

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

Comments

5

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

Comments

1

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?>) {
   // ...
}

Generic Inheritance

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.

Upper bounded wildcard

Comments

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.