In the following code example I expect to have 1 as a return value of method testM()
. But due to the exception in TestAutoCloseable.close()
method I get unexpected behaviour.
My question is: "Is it a normal behaviour of JVM?"
public static void main(String[] args) {
ProgrammerTwo programmerTwo = new ProgrammerTwo();
System.out.println(programmerTwo.testM());
}
int testM() {
try (TestAutoCloseable closable = new TestAutoCloseable()) {
System.out.println("Do first return");
return 1;
} catch (IOException e) {
System.out.println("handled");
}
System.out.println("Do something, that shouldn't do if first return have happened");
return 2;
}
static class TestAutoCloseable implements AutoCloseable {
@Override
public void close() throws IOException {
throw new IOException();
}
}
Because if it is normal behaviour, we shouldn't use return or break statements in try with resources statement. It should be anti-pattern.
The details of how the try-with-resources
statement works are in this section from the JLS. In your case, it is an extended try-with-resources
since it has a catch
clause as defined in the below quote (note the highlighted statement at the end).
A try-with-resources statement with at least one catch clause and/or a finally clause is called an extended try-with-resources statement.
The meaning of an extended try-with-resources statement:
try ResourceSpecification Block [Catches] [Finally]
is given by the following translation to a basic try-with-resources statement nested inside a try-catch or try-finally or try-catch-finally statement:
try {
try ResourceSpecification <--- exception thrown in this basic try-with-resources
Block
}
[Catches]
[Finally]
The effect of the translation is to put the resource specification "inside" the try statement. This allows a catch clause of an extended try-with-resources statement to catch an exception due to the automatic initialization or closing of any resource.
This means that closing the resource occurs within the body of the outer try
block, causing an exception to be thrown and handled in the catch
block, and control resumes to the statements after the extended try-with-resources
statement.
In effect, the whole method testM
is equivalent to:
int testM() {
try {
final TestAutoCloseable closable = new TestAutoCloseable();
Throwable #primaryExc = null;
try {
System.out.println("Do first return");
return 1;
} catch (Throwable #t) {
#primaryExc = #t;
throw #t;
} finally {
if (closable != null) {
if (#primaryExc != null) {
try {
closable.close();
} catch (Throwable #suppressedExc) {
#primaryExc.addSuppressed(#suppressedExc);
}
} else {
closable.close();
}
}
}
} catch (IOException e) {
System.out.println("handled");
}
System.out.println("Do something, that shouldn't do if first return have happened");
return 2;
}