Skip to content

Quirk: finally overriding return and swallowing exceptions - #109

Merged
mperor merged 1 commit into
masterfrom
DAILY/96-finally-override-return
Nov 4, 2026
Merged

mperor merged 1 commit into
masterfrom
DAILY/96-finally-override-return

Conversation

@mperor

@mperor mperor commented Nov 3, 2026 •

Copy link
Copy Markdown
Owner

What you'll learn

  • a return inside finally silently replaces the value already queued by try's return
  • a return inside finally discards any exception in flight, so the caller never sees it
  • try's return expression is evaluated and captured before finally runs, so reassigning a primitive local afterwards has no effect on the returned value
  • unlike a primitive, a returned object is a reference: mutating the same object in finally is visible to the caller
  • try-with-resources behaves differently: the primary exception from the body propagates, and a failure from close() is attached as a suppressed exception instead of replacing it

Why it matters

return/break/continue in finally is a classic footgun — it can mask bugs by silently discarding exceptions, and relying on it is almost always a mistake. Knowing how value capture works also clarifies why mutating an object differs from reassigning a primitive during cleanup.

Read more

JLS §14.20.2 — Execution of try-finally

Quiz

static String test() {
    try {
        throw new IllegalStateException("boom");
    } finally {
        return "finally";
    }
}

What happens when test() is called?

Answer It returns `"finally"` normally — no exception is thrown. The `return` in `finally` always wins: it discards the exception propagating out of `try` before it reaches the caller.

Closes #96

@mperor mperor added the daily Queued for the daily learning PR label Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

Test Results

292 tests  +5   289 ✅ +5   6s ⏱️ -1s
 82 suites +1     3 💤 ±0 
 82 files   +1     0 ❌ ±0 

Results for commit 6f7e53c. ± Comparison against base commit ea35b4f.

This pull request skips 1 and un-skips 1 tests.
pl.mperor.lab.java.design.pattern.creational.singleton.SingletonTest ‑ testLazyInitialized()
pl.mperor.lab.java.design.pattern.creational.singleton.SingletonTest ‑ testThreadSafe()

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

@mperor ⏰ This daily topic is still waiting for your review.

@mperor
mperor merged commit b44fe65 into master Nov 4, 2026
2 checks passed
@mperor
mperor deleted the DAILY/96-finally-override-return branch November 4, 2026 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

daily Queued for the daily learning PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Quirk: finally overriding return and swallowing exceptions

2 participants