0
votes

Consider the following example:

class Foo<T>

fun <T> Foo<Iterable<T>?>.bar(i: Iterable<T>) {
    ...
}

Class Foo has an extension method, bar which requires a receiver of Iterable<T>?

Consider the following use-case:

val x = listOf(123, 456)
val f = Foo<Iterable<Int>>()

f.bar(x)

Compiler error on f:

Type mismatch

Required: Foo<Iterable<Int>?>

Found: Foo<Iterable<Int>>

I could understand this not working the other way around, trying to pass a nullable type to a non-nullable receiver, but I don't understand why I can't pass a non-nullable to a nullable receiver.

Essentially I'm trying to say "The receiver may be null, but in this case, I can guarantee it isn't"

Any thoughts on how to fix this, so the extension method will allow both nullable and non-nullable types?

Note: I cannot change val f = Foo<Iterable<Int>> to val f = Foo<Iterable<Int>?> because this is automatically decided based on the type of a property.

3

3 Answers

3
votes

Just add out modifier and it will work:

class Foo<out T>

We use out modifier to indicate covariance (similar to "? extends T" in Java). Covariance - is the ability to change the generic type argument from a class to one of its parents, i.e. assign List<String> to List<Any>.

Here are docs about generics.

3
votes

Alternatively to @Sergey's answer, you can employ use-site variance rather than declaration-site variance (as class Foo<out T> will limit the use of T and affect all usages) and add the out modifier at the extension declaration:

fun <T> Foo<out Iterable<T>?>.bar(i: Iterable<T>) { /* ... */ }

(runnable sample)

The out modifier means that the extension will accept not just exactly Foo<Iterable<T>?> but any subtype of nullable Iterable typed with T as well, such as List<T>?, Set<T>?, and also, as not-null types are treated as subtypes of the nullable ones, the extension will accept not-null counterparts of the Iterable types.

1
votes

You can achieve the same effect that Hotkey and Sergey described like this:

fun <T, S: Iterable<T>?> Foo<S>.bar(i: Iterable<T>) { /* ... */ }

It is more redundant but might be easier to understand. Since you define Iterable<T>? as upper bound of S, any subtype including Iterable<T> is allowed now too.

After grasping the concept, I would still go with out Iterable<T>? because it more concise.