Explain Codes LogoExplain Codes Logo

Why does changing the returned variable in a finally block not change the return value?

java
control-flow
finally-block
return-statement
Anton ShumikhinbyAnton Shumikhin·Dec 13, 2024
TLDR

In Java, the return value is sealed the moment a return statement is encountered. Consequently, subsequent modifications in a finally block have no impact on it. Consider the following condensed example:

public int returnTest() { int value = 10; try { return value; // Java says "My mind is made up. No take-backs!" } finally { value = 20; // Java's reaction: "That's cute. See above comment." } }

Even though value changes in the finally block, the method still returns 10. This is because, funnily enough, that's the value Java committed to at the return moment.

Under the hood: How return and finally behave

To really grasp what's going on here, you need to dive into the nitty-gritty of Java's control flow and how the return statement dances with the finally block.

Stack: A reliable buddy

It's all about understanding the role of the JVM stack. When a return statement is nearly executed, the stack say, "I've got this!" and stores the return value, hence any subsequent changes to local variables can't touch it!

Interesting case of abrupt termination

When the try block is abruptly ended via a return statement, the machine is like, "I’m outta here! Hold my final value, dear stack!" and sprints towards finally for its cleanup job.

The true intentions of finally

The finally block is Java's neat freak. It's designed for cleanup, sweeping away resources like files or connections, not messing around with return values.

The tale of mutable objects

It gets interesting in the case of mutable objects. When calling a mutable object that is altered in the finally block, you can see the aftermath:

public StringBuilder returnTest() { StringBuilder sb = new StringBuilder("initial"); try { return sb; } finally { sb.append(" changed"); // Mind = 🤯 } }

In this case, the caller is startled to see sb with " changed" trailing, simply because the object referenced by sb was mutated directly.

Deep diving: Edge cases and guidelines

Sneaky override by finally's return

A surprising turn happens when the finally block sports a return statement of its own, this ends up usurping the try block's return value:

public int returnTest() { try { return 10; // So sure about returning 10 } finally { return 20; // Plot twist: Returns a 20 even if try-block cried "10" } }

Crafty JVM optimizations

It's rather fascinating how, under some conditions, the mighty JVM can simply ignore or optimize away the finally block once it establishes that the latter doesn't rock the boat with the control flow.

The holy Java specification

The Java Language Specification (JLS) is your definitive guide to how try, catch and finally statements are meant to perform. It's your guide to the nitty-gritty rules.

Developer's best practices

To fend off unanticipated results, devs should stay within safe bounds by following best practices. Use finally blocks for cleanups, resource management, not for changing the return values path or altering the program flow.