mrb
mrb

Reputation: 3330

Return type mismatch when throwing exception in finally

I am using Kotlin 1.2.60.

val someString: String = try {

    String.format("Okay %s", "there") // or any function that returns String

} catch(exception: Exception) {

    try {
        // fun incrementErrorCount() { ... }
        incrementErrorCount() // [1] Error: Type mismatch: Inferred type is Unit but String was expected
    } finally {
        throw exception
    }

    // throw exception // [2] Fixes the type mismatch when uncommented, but raises Warning: Unreachable code
}

I get a Type mismatch error at 1 because it expects incrementErrorCount() to return a String — but the return type shouldn't matter, because it is always going to re-throw exception anyway.

I added 2, which fixes the Type mismatch error, but raises an Unreachable code warning because the function never proceeds beyond the throw in finally.

What am I doing wrong? How can I get this to compile without errors or warnings?

Upvotes: 2

Views: 2576

Answers (3)

msrd0
msrd0

Reputation: 8371

See the Kotlin Reference for try-catch-finally and you'll see:

The returned value of a try-expression is either the last expression in the try block or the last expression in the catch block (or blocks). Contents of the finally block do not affect the result of the expression.

So this means that while your intention is correct, and there is no way to return from your second try block, the Kotlin compiler will still take Unit as it's return value and not modify it due to the specification of the finally block.

To make the compiler aware of the fact that the catch block will always return Nothing, you need to move the throw out of the finally block.

Upvotes: 2

Martin Stone
Martin Stone

Reputation: 13007

I'm assuming your desired logic is that if String.format fails, you let the exception be thrown, but also increment your error count. In that case, I think you need (untested)...

val someString: String = try {

    String.format("Okay %s", "there") // or any function that returns String

} catch(exception: Exception) {

    try {
        // fun incrementErrorCount() { ... }
        incrementErrorCount()
    } 
    catch (dontCare: Exception) {
    }

    throw exception 

}

Upvotes: 0

zsmb13
zsmb13

Reputation: 89568

try-catch is an expression in Kotlin. If the try block runs successfully without throwing anything, its last value is returned. If it throws an exception, the catch block's last value is returned.

In your case, if String.format doesn't throw an exception, its result is returned from the first try-catch, and you're good. This is the easy route.

If String.format throws an exception, the last expression of the catch block is returned, which is the second, nested try-finally itself. This can end two ways:

  • If the nested try block runs successfully, its last expression (in this case, incrementErrorCount) is what it evaluates to, and then the result of that call would be assigned to someString. This is the type error you're getting, it looks like incrementErrorCount doesn't return a String to assign.
  • If the nested try fails, you throw an exception, cancelling the assignment of someString altogether.

Adding line [2] fixes your error because the nested try-catch isn't used as the value to assign to someString, it's just ran and the throw expression cancels the entire assignment of someString.

Upvotes: 0

Related Questions