Why would this piece of Kotlin code not compile? Needless to say return type is Unit (or void in Java)
The error is: 'if' must have both main and 'else' branches if used as an expression
fun test() =
try {
if (true) {
}
// no else clause
} finally {
print("")
}
It doesn't matter which combination of the try statement I implement (try with catch or try with finally or all of them)
fun test() =
try {
if (true) {
}
// no else clause
} catch (e: Exception) {
print("")
} finally {
print("")
}
Strangely this compiles:
fun test() =
try {
if (true) {
}
print("print something")
} catch (e: Exception) {
print("")
} finally {
print("")
}
Using latest IntelliJ.
try/catch/finally, when, and if/else can be used either as a statement or as an expression. When used as an expression, they have to return something. You are forcing the compiler to treat it as an expression by putting = in front of it.
You return something in try/catch by making it the last thing in your try block and in any catch blocks.
if by itself cannot be an expression. It has to be followed by else to be an expression.
Since the if block is the last thing in your try clause of your try/catch expression, it has to be an expression, because the last thing in the block represents what it returns.
The compiler is complaining because the last thing in your try/catch expression's try block is not an expression. It's an if statement.
When you add println() at the end, that's an expression, so it can be interpreted fine by the compiler. println() evaluates to Unit.
As #Gidds says in the comment below, the whole issue stems from making this a expression in the first place. If you have nothing useful to return (Unit), then make it a statement so you don't have to worry about pleasing the compiler. When you try to make it an expression, the compiler is pedantic because it's trying to prevent you from writing something that doesn't make sense to write if you're trying to return something useful.
Also, if you deliberately express that your expression should evaluate to Unit using : Unit =, then the compiler doesn't enforce that it really has to evaluate as an expression.
Related
I have Java background and recently I started to learn Kotlin. Right now I'm reading a book "Programming Kotlin" and I reached a snippet with try-catch expression. It caused me to write some simple function to compare the way it works in Java and in Kotlin.
First function:
fun tryExpExplicit(throwing: Boolean): Int {
return try {
if (throwing) {
throw RuntimeException()
}
return 1
} catch (e: Exception) {
return 2
} finally {
return 3
}
}
works as I expected and always returns 3.
Unexpectedly, when I use implicit return, the behaviour is different
fun tryExpImplicit(throwing: Boolean): Int {
return try {
if (throwing) {
throw RuntimeException()
}
1
} catch (e: Exception) {
2
} finally {
3
}
}
and 3 is never returned.
Why do these two functions work differently?
The difference from behaviour in Java is because try ... catch ... finally is an expression in Kotlin, not a statement.
The way that a "try-expression" is evaluated, is defined as follows in the specification:
The try-expression evaluation evaluates its body; if any statement in the try body throws an exception (of type E), this exception, rather than being immediately propagated up the call stack, is checked for a matching catch block. If a catch block of this try-expression has an exception parameter of type T:>E , this catch block is evaluated immediately after the exception is thrown and the exception itself is passed inside the catch block as the corresponding parameter. [...]
If there is a finally block, it is evaluated after the evaluation of all previous try-expression blocks
The value of the try-expression is the same as the value of the last expression of the try body (if no exception was thrown) or the value of the last expression of the matching catch block (if an exception was thrown and matched). All other situations mean that an exception is going to be propagated up the call stack, and the value of the try-expression is undefined.
Note: as described, the finally block (if present) is always executed, but has no effect on the value of the try-expression.
So when throwing is true, the try, catch, and finally blocks are all executed, but the value of the try-expression is the value of the last expression in the catch block. This explains the behaviour in both the "explicit" and "implicit" cases.
In the "explicit return" case, return 2 is executed, but the method can't return there - the finally block still has to run! Then return 3 is executed, and now the method returns. Notably, the outer return is never executed. You can delete the outer return and start the method with try { ... and get the same result.
In the "implicit return" case, 2 is evaluated, and no side effects happen since it is just a literal. Then the finally block runs, and 3 is evaluated, and again no side effects happen since it is just a literal. Now we have finished evaluating the whole try-expression, and according to the spec, the value of the expression should be what we evaluated in the catch block - i.e. 2. And now we execute return 2.
Side note: return ... are also expressions, and they have the type of Nothing (the subtype of every type), because they never evaluate to anything. This is why you are able to write return 1 etc as the last line in the try-expression blocks in the "explicit return" case. In that case, the try-expression actually has a type of Nothing, and the outer return is actually returning Nothing. This is fine, because Nothing is a subtype of Int.
In the documentation here, it says that when try/catch is used an expression:
The contents of the finally block don't affect the result of the expression.
In the first example, even though your finally block is not affecting the expression's evaluation, it's short-circuiting the expression entirely by returning directly.
In the second example, finally isn't doing anything, and since it doesn't affect the expression, the value is ignored.
In practice, you'd never write code to return from a try and/or catch block if you're returning from a finally block, since it would be useless code.
You can do something like this in kotlin
fun tryExpExplicit(throwing: Boolean): Int {
return runCatching {
// do something
}.onFailure { exception ->
// similar to catch
}.onSuccess { data ->
// similar to finally
}.getOrThrow() // get the data or throw
}
internally runCatching is using try/catch blocks similar in java. you can think of
runCatching as try
.onFailure as catch
.onSuccess as finally
you could also use it's extension to return the value
.getOrThrow if you want to return the data or throw an exception
.getOrNull if you want to return the data or null if it throw an exception
.getOrElse if you want to return the data or else if it throw an exception
.getOrDefault if you want to return the data or default if it throw an exception
I want to run cleanup code after a certain block of code completes, regardless of exceptions. This is not a closeable resource and I cannot use try-with-resources (or Kotlin's use).
In Java, I could do the following:
try {
// ... Run some code
} catch(Exception ex) {
// ... Handle exception
} finally {
// ... Cleanup code
}
Is the following Kotlin code equivalent?
runCatching {
// ... Run some code
}.also {
// ... Cleanup code
}.onFailure {
// ... Handle exception
}
Edit: added boilerplate exception handling - my concern is with ensuring the cleanup code runs, and maintainability.
There is one important difference, where the code inside runCatching contains an early return. A finally block will be executed even after a return, whereas also has no such magic.
This code, when run, will print nothing:
fun test1()
runCatching {
return
}.also {
println("test1")
}
}
This code, when run, will print "test2":
fun test2() {
try {
return
} finally {
println("test2")
}
}
There is one big difference between both code samples. try...finally propagates exceptions while runCatching().also() catches/consumes them. To make it similar you would have to throw the result at the end:
runCatching {
// ... Run some code
}.also {
// ... Cleanup code
}.getOrThrow()
But still, it is not really 1:1 equivalent. It catches all exceptions just to rethrow them. For this reason, it is probably less performant than simple try...finally.
Also, I think this is less clear for the reader. try...finally is a standard way of dealing with exceptions. By using runCatching() just to immediately rethrow, you actually confuse people reading this code later.
Your question sounded a little like you believed Kotlin does not have try...finally and you need to search for alternatives. If this is the case, then of course Kotlin has try...finally and I think you should use it instead of runCatching().
As per Kotlin's doc for runCatching:
Calls the specified function block and returns its encapsulated result if invocation was successful, catching any Throwable exception that was thrown from the block function execution and encapsulating it as a failure.
Even if finally always runs after a try block and also always runs after a runCatching, they do not serve the same purpose.
finally doesn't receive any argument and cannot operate on the values of the try block, while also receives the Result of the runCatching block.
TLDR; .runCatching{}.also{} is a more advanced try{}finally{}
There is also a difference in what is the result of evaluating the expression.
Consider the following code:
fun main() {
val foo = try {
throw Exception("try")
} catch(e: Exception) {
"catch"
} finally {
"finally"
}
val bar = runCatching{
throw Exception("runCatching")
}.also{
"also"
}.onFailure {
"onFailure"
}
println(foo)
println(bar)
}
The output will be:
catch
Failure(java.lang.Exception: runCatching)
https://pl.kotl.in/a0aByS5l1
EDIT:
An interesting article that points out some differences as well:
https://medium.com/#mattia23r/a-take-on-functional-error-handling-in-kotlin-515b67b4212b
Now let’s give a second look at the implementation of runCatching in the gist above. What does it do? It catches everything.
In this case, it goes even further: it catches all Throwables. For those not knowing, Throwable is everything that can go after a throw keyword; it has two descendants: Exceptions and Errors. We haven’t mentioned Errors so far; Errors usually represent something wrong that happened at a lower level than your business logic, something that can’t usually be recovered with a simple catch.
I'm working on a kotlin web backend and have something like this:
try {
val uuid = UUID.fromString(someString)
} catch (e: IllegalArgumentException) {
throw BadRequestException("invalid UUID")
}
doSomething(uuid)
The code above doesn't compile since uuid is unresolved outside the try block.
Alternatives I can imagine are:
move doSomething(uuid) inside the try block, but I'd rather avoid that so I don't accidentally catch some other potential IllegalArgumentException thrown by doSomething (if that happens for whatever reason I want things to fail and get a 500 in my logs so I can investigate)
use a nullable var instead and initialize it to null but that seems a bit ugly?
This throw BadRequestException pattern is working well otherwise so I don't want to change the return type of the method or something like that in order to avoid throwing.
Is there a better / more elegant / recommended pattern for this in Kotlin?
In Kotlin, try/catch can be used as an expression. Branches that throw don't affect the resolved type. So you can write:
val uuid = try {
UUID.fromString(someString)
} catch (e: IllegalArgumentException) {
throw BadRequestException("invalid UUID")
}
I'm using the buildSequence function in Kotlin. How do I end the iteration in the middle of the function? I'm looking for something similar to C#'s yield break statement.
My code looks something like the following. I'm stuck at the TODO.
fun foo(list:List<Number>): Sequence<Number> = buildSequence {
if (someCondition) {
// TODO: Bail out early with an empty sequence
// return doesn't seem to work....
}
list.forEach {
yield(someProcessing(it))
}
}
EDIT
Apparently, I misdiagnosed the source. The issue is not returning from the buildSequence function. The following works for me:
fun foo(list:List<Number>): Sequence<Number> = buildSequence {
return#buildSequence
list.forEach {
yield(someProcessing(it))
}
}
EDIT 2
The issue is that I put the return in a local helper function that validates data at multiple points in the buildSequence (Hence the helper function). Apparently I'm not able to return from buildSequence within the helper function. The error message was not terribly helpful...
Just use return#buildSequence, which is a labeled return from lambda, while an unlabeled return would mean 'return from the function foo'.
See also: Whats does “return#” mean?
Since Kotlin v 1.3.x preferred sequence syntax changed. (buildSequence is replaced by kotlin.sequences.sequence)
Updated "early return from generator" code snippet (includes try-catch and == null early return examples) for post 1.3.x Kotlin:
// gen# is just a subjective name i gave to the code block.
// could be `anything#` you want
// Use of named returns prevents "'return' is not allowed here" errors.
private fun getItems() = sequence<Item> gen# {
val cursor: Cursor?
try {
cursor = contentResolver.query(uri,*args)
} catch (e: SecurityException) {
Log.w(APP_NAME, "Permission is not granted.")
return#gen
}
if (cursor == null) {
Log.w(APP_NAME, "Query returned nothing.")
return#gen
}
// `.use` auto-closes Closeable. recommend.
// https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/use.html
cursor.use {
// iterate over cursor to step through the yielded records
while (cursor.moveToNext()) {
yield(Item.Factory.fromCursor(cursor))
}
}
}
(Thx for all the prior posts that helped me get on "named return" track.)
I'm currently learning Kotlin, and one way I'm doing it is by automatically converting Java code to Kotlin and studying the results. One piece of Java code I tried to convert is the following static block in a class that tries to ensure that assertions are enabled:
static {
boolean assertsEnabled = false;
assert assertsEnabled = true;
if (!assertsEnabled)
throw new AssertionError("Please enable assertions!");
}
This relies on the assertsEnabled = true expression as an argument to assert. In Java, assignments are expressions. In Kotlin, they're not, and so this can't be converted. Is there any other way to do it?
Unfortunately, Kotlin doesn't have the assert keyword with its special semantics. Instead it has this function:
inline fun assert(value: Boolean, lazyMessage: () -> Any)
You can see that the expression passed as the first argument is evaluated unconditionally. This means you can't achieve the same lightweight check as in Java; you have to trigger an actual assertion failure to make sure.
So you need a check as suggested by #Zoe:
try {
assert(false)
throw IllegalStateException("Please enable assertions!")
} catch (e: AssertionError) {
// Things are looking good; carry on
}
If you insist on throwing an AssertionError instead of IllegalStateException, you can use a boolean variable for that.
var assertionsAreDisabled = false
try {
assert(false)
assertionsAreDisabled = true
} catch (e: AssertionError) {
// Things are looking good; carry on
}
if (assertionsAreDisabled) {
throw AssertionError("Please enable assertions!")
}