skip to content

Can a try-with-resources use a resource variable declared outside the try header? Explain effectively-final resources (Java 9+).

level: seniorimportance: should knowfreq 40%

answer

  1. Java 9 / JEP 213 feature
  2. name an existing variable in try(...) instead of declaring
  3. variable must be final or effectively final
  4. effectively final = assigned once, never reassigned
  5. ensures the exact object is the one closed

basics

~20 s

Yes, since Java 9. If a resource is already stored in a final or effectively-final variable, you can just name that variable inside try(...) instead of declaring a new one, and Java still closes it automatically.

solid answer

~50 s

Before Java 9, every resource had to be declared inside the try parentheses, so a resource passed in as a parameter had to be reassigned to a fresh local just to use it. Java 9 (JEP 213) relaxed this: you may reference an existing variable in the try header as long as it is final or effectively final, meaning it is assigned once and never reassigned afterward. The variable is closed automatically just like a declared resource. This is convenient for resources you receive from elsewhere (a method parameter, a field-held value). The effectively-final requirement exists for safety: the try-with-resources must close exactly the object it was given, so the reference must not change between the try header and the close() call, which a reassignment could break. You can list several such variables, separated by semicolons, and they still close in reverse order.

code

java · 9 lines
java
// Java 9+: use an existing effectively-final variable
void copy(InputStream src) throws IOException {  // src never reassigned
    try (src) {                                  // legal since Java 9
        src.transferTo(System.out);
    }                                            // src.close() called automatically
}

// This would NOT compile: reassigning makes it not effectively final
// InputStream s = open(); s = open(); try (s) { ... } // compile error

go deeper

for a junior

Knows that since Java 9 you can put an existing variable in try(...) and it still gets closed.

for a middle

States the final/effectively-final requirement and can write both the declared and the existing-variable forms.

for a senior

Explains effectively-final precisely and the safety reason the restriction exists (closing the exact captured object).

for a principal

Discusses the API/readability tradeoffs of passing in vs declaring resources and how the rule mirrors lambda capture semantics across the language.

## The Java 7/8 limitation In Java 7 and 8 the resource had to be **declared** in the try header — a new variable with `= ...` initializer: ```java try (Resource r = new Resource()) { ... } ``` If you already had a resource (say it was passed into your method), you could not just name it; you had to copy it into a dummy local: ```java void use(Resource r) { try (Resource ignore = r) { // awkward extra variable ... } } ``` ## Java 9 relaxation (JEP 213) Java 9 allows the try header to reference an **existing variable** directly, provided it is **final or effectively final**: ```java void use(Resource r) { // r is effectively final (never reassigned) try (r) { // legal since Java 9 ... } // r.close() called automatically } ``` No new variable needed. The named variable is treated as a resource and closed on exit, in the usual reverse order if there are several. ## What 'effectively final' means A variable is **final** if declared with the `final` keyword. It is **effectively final** if it is *not* declared `final` but is never reassigned after its initial assignment — the compiler can prove it could have been `final`. (This is the same concept that lets a local variable be captured by a lambda.) Examples: ```java Resource a = get(); // assigned once, never reassigned -> effectively final -> OK in try(a) Resource b = get(); b = get(); // reassigned -> NOT effectively final -> try(b) is a compile error ``` ## Why the restriction exists The statement must close **exactly the object** that the variable referred to when the try began. If the variable could be reassigned, it might point at a different object (or null) by the time `close()` runs, so the wrong object — or nothing — would be closed, defeating the safety guarantee. Requiring final/effectively-final makes the referenced object stable, so the compiler can reliably capture it and close it. ## Multiple existing resources You can mix or list several, semicolon-separated: ```java try (a; b) { ... } // both effectively final; closed b then a ``` ## Glossary - **Effectively final**: a non-final variable that is never reassigned after initialization, so it behaves as if final. - **Resource specification / try header**: the parenthesized part after `try`. - **JEP**: a JDK Enhancement Proposal — a documented feature change. Summary: since Java 9 you can pass an existing final or effectively-final variable straight into `try(...)`; it is closed like any resource, and the final-ness requirement guarantees the right object is the one closed.

  • What is the difference between 'final' and 'effectively final'?
    final is the explicit keyword; effectively final means the variable is never reassigned after its initial assignment, so the compiler treats it as if it were final even without the keyword.
  • Why must the external resource variable be final or effectively final?
    So the object closed at the end is guaranteed to be the same one the try header captured. If reassignment were allowed, the variable could point to a different object or null by close time, closing the wrong thing or nothing.

saying these in an interview costs you the question

  • Saying any external variable works (it must be final/effectively final)
  • Claiming this was always allowed (it arrived in Java 9)
  • Reassigning the variable and expecting try(var) to still compile
  • Confusing this with try-with-resources requiring a NEW declaration always

context