I want to create function which will try to call main function. If it is ok return the resutl of this function outside. But if it is failed - call another function(fallback function) and return its result outside. If both failed - exception of second function should be propagated.
I've creare dollowing function:
fun<T> callWithFallback(
main: () -> T,
fallback: () -> T
): T = try {
main()
} catch (e: Exception) {
logger.warn(e) { "main call failed: ${e.message}" }
if (/*some condition*/) {
try {
fallback()
} catch (e: LDAPException) {
logger.warn(e) { "Repeated call failed: ${e.message}" }
throw e
}
}
throw e
}
in case of main is failed - fallback()
is called but even if it is successsful - exception is thrown (last throw e
is called)
I tried to add return
fun<T> callWithFallback(
main: () -> T,
fallback: () -> T
): T = try {
main()
} catch (e: Exception) {
logger.warn(e) { "main call failed: ${e.message}" }
if (/*some condition*/) {
try {
return fallback()
} catch (e: LDAPException) {
logger.warn(e) { "Repeated call failed: ${e.message}" }
throw e
}
}
throw e
}
But it leads to compile error.
How can I fix it ?
The problem of your current implementation comes from the if
statement without else
in your first catch
clause :
To know what to return from the catch, Kotlin compiler looks at the last statement it contains. Here, it is throw e
, not the if that contains the wanted fallback.
As your if
has no else
branch, it is considered a statement instead of an expression. Therefore, it cannot return anything. You can test this assertion by removing throw e
from the catch
clause. The compiler will then issue a warning to explain it cannot return anything from the catch
clause:
'if' must have both main and 'else' branches if used as an expression
To fix your problem, you can move the throw e
statement at the bottom of your function in an else clause, like so:
fun<T> callWithFallback(
main: () -> T,
fallback: () -> T
): T = try {
main()
} catch (e: Exception) {
logger.warn(e) { "main call failed: ${e.message}" }
if (/*some condition*/) {
try {
fallback()
} catch (e: LDAPException) {
logger.warn(e) { "Repeated call failed: ${e.message}" }
throw e
}
} else {
throw e
}
}
Doing so makes the if/else
the latest statement in the catch
clause, and as it is an expression (due to the presence of the else branch), its result is considered the return value of the catch.