Example:
sealed interface Foo<out T> {
val value: T
}
data class Bar<out K: List<Int>>(override val value: K): Foo<K>
fun <T> processFoo(foo: Foo<T>) {
when (foo) {
is Bar -> foo.value.forEach(::println)
}
}
Fails with:
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public inline fun <T> Iterable<TypeVariable(T)>.forEach(action: (TypeVariable(T)) -> Unit): Unit
defined inkotlin.collections
public inline fun <K, V> Map<out TypeVariable(K), TypeVariable(V)>.forEach(action: (Map.Entry<TypeVariable(K), TypeVariable(V)>) -> Unit): Unit
defined inkotlin.collections
Why this fails? I expect that if foo
is of type Bar
then we know that T
is a subtype of List<Int>
. So we should be able to call forEach
on it. Am I wrong?
CodePudding user response:
This problem is simply caused by a typo in your code.
If you replace is Bar
with is Bar<*>
, the compiler is able to infer that T
is a List<Int>
in that context and the code compiles.
CodePudding user response:
I expect that if foo is of type Bar then we know that T is a subtype of List. So we should be able to call forEach on it.
Yes, that is true. But T
could also implement Map<K, V>
at the same time as it implements List<Int>
(I don't know of such a type, but it theoretically could exist), in which case you would also be able to call this extension function:
inline fun <K, V> Map<out K, V>.forEach(
action: (Entry<K, V>) -> Unit)
See all the different forEach
es here.
To specifically call the forEach
defined for Iterable
, just do a cast:
// could also cast to List<Int> here - that's a completely safe unchecked cast
(foo.value as List<*>).forEach(::println)
An alternative is to use is Bar<*>
, but a (very) slight drawback of this is that, as <*>
projects the type of foo.value
to be List<Int>
, you lose the T
. You won't be able to use foo.value
in places where a T
is expected.
A contrived example would be:
fun <T> processFoo(foo: Foo<T>): T {
return when (foo) {
// you can't return foo.value when you are expected to return T
is Bar<*> -> foo.value.also {
it.forEach(::println)
}
}
}