Let's say I have a simple class Foo
with a nullable String?
data class Foo(
val bar: String?
)
and I create a simple function capitalize
fun captitalize(foo: Foo) = when {
foo.bar != null -> runCatching { foo.bar.capitalize() }
else -> ""
}
which works fine, because the compiler infers that foo.bar cannot be null eventhough it's type is nullable. But then I decide to write the same function as an extension of Foo
fun Foo.captitalize2() = when {
bar != null -> runCatching { bar.capitalize() }
else -> ""
}
and all of a sudden the compiler is no longer able to infer that bar is not null, and IntelliJ tells me that "only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable reciever of type String?"
Can anyone explain why?
I think it's because in the first case you are calling this function:
public inline fun <R> runCatching(block: () -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
but in the second case you are calling function with receiver:
public inline fun <T, R> T.runCatching(block: T.() -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
For me, it looks like an issue in the Kotlin compiler because if you inline code of this function by yourself it will work fine:
fun Foo.captitalize2() = when {
bar != null -> try {
Result.success(bar.capitalize())
} catch (e: Throwable) {
Result.failure<String>(e)
}
else -> ""
}
btw, if I were you I would like to write my capitalize2
function like this :)
fun Foo.captitalize2() = bar?.capitalize() ?: ""
So, finally I found an alternative approach that allows us to use runCatching
without having the problem you shows.
As in my comment to the answer of @Andrei Tanana, in your code type parameters of fun <T, R> T.runCatching(block: () -> R) : Result<R>
are inferred as <Foo, String>
and the compiler can't use the information that this.bar
is not null
.
If you rewrite the capitalize2
function as follows
fun Foo.capitalize2(): Serializable = when {
bar != null -> bar.runCatching { capitalize() }
else -> ""
}
T
is inferred as String
(thanks of the bar != null
case of the when
expression) and the compiler does not complain about this.capitalize()
invocation in the block passed to runCatching
.
I hope this can help you, both as an approach than allows you to solve the problem and as explanation of the problem itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With