Java SE 8 Programmer I (OCAJP 8) study materials
Java SE 8 Programmer I (OCAJP 8) study resources

Import Pipeline: Overriding Throws Clauses and a Finally That Returns

Java SE 8 Programmer I (OCAJP 8) · Put your knowledge to work in context.

All case studies

Import Pipeline: Overriding Throws Clauses and a Finally That Returns

An import pipeline declares abstract class Importer { abstract String load(String path) throws IOException; }. Three subclasses follow. CsvImporter overrides load with throws FileNotFoundException. XmlImporter overrides it with throws Exception. JsonImporter overrides it with no throws clause, but its body calls Files.readAllBytes(Paths.get(path)), which throws IOException. A test harness is written as: try { importer.load("data.csv"); return "loaded"; } catch (IOException e) { return "io"; } finally { return "done"; }. The team expects "io" when the file is missing, but the harness returns "done" every time - even when no exception occurs. Two more catch blocks are proposed: catch (IOException | FileNotFoundException e) and, in another method, catch (Exception e) { ... } catch (IOException e) { ... }.

1. For each of the three subclasses, does the `load` method compile? State the rule for throws clauses in overrides.

Read the worked answer
  • CsvImporter: compiles. An override may declare the same checked exception, a subclass of it, or none at all. FileNotFoundException is a subclass of IOException, so callers that only handle IOException are still safe.
  • XmlImporter: does not compile. Declaring throws Exception is broader than the parent's IOException; a caller holding an Importer reference expects to handle only IOException, so the override would break that contract. Removing Exception or replacing it with IOException/FileNotFoundException fixes it.
  • JsonImporter: does not compile as written. Files.readAllBytes throws the checked IOException. Every checked exception a method body can throw must be caught inside the method or declared in its throws clause. Saying nothing while calling a method that throws IOException is an error. Either add throws IOException (allowed - identical to the parent) or wrap the call in try/catch and handle/rethrow.

Related rules: unchecked exceptions (RuntimeException and its subclasses) may always be thrown without declaration and may be declared freely; a private method is not inherited so its signature constraints do not apply; constructors are not bound by the parent's throws clause, only super(...)/this(...) calls inside them are.

2. Why does the harness always return "done", and what exactly is lost by the `return` in the `finally` block?

Read the worked answer

Because a return inside finally overrides every other exit from the try statement.

Execution order: the try block runs importer.load(...). Whether it completes normally, returns, or throws, the finally block always runs before control leaves the statement. When finally executes return "done", that return wins:

  • If load succeeded, the pending return "loaded" is discarded and "done" is returned.
  • If load threw IOException, the catch block computes return "io", but finally still runs first and its return discards that value and returns "done" instead.
  • If load threw an unchecked exception (or any exception the catch does not match), the finally return swallows the exception entirely - the caller sees a normal return of "done" and never learns about the failure.

So the method is effectively return "done"; plus side effects. finally should be used for cleanup (closing resources, releasing locks), never for return, break or continue in code that carries a result or an exception. The modern fix is try-with-resources, which closes resources without suppressing results or exceptions.

3. Do the two proposed multi-catch/ordering snippets compile? Explain both, and note any restrictions that apply to a multi-catch parameter.

Read the worked answer

Neither compiles.

catch (IOException | FileNotFoundException e) is a compile error. Alternatives in a multi-catch must be disjoint; they may not be related by subclassing, because the parameter would then have two possible types and the compiler cannot decide the type of e. FileNotFoundException is a subclass of IOException, so the alternatives are related. Writing catch (FileNotFoundException e) first and catch (IOException e) after is the correct way to give the subclass special handling; alternatively catch only IOException.

catch (Exception e) { ... } catch (IOException e) { ... } is also a compile error - this time the later block is unreachable. IOException is a subtype of Exception, so every IOException is already handled by the first block. Unreachable catch blocks are compile-time errors, not warnings. Ordered from most specific to most general (FileNotFoundException, then IOException, then Exception) it is legal.

Restrictions on a multi-catch parameter: there is exactly one parameter, its type is the least upper bound of the alternatives, it is implicitly final (so e = new IOException(); inside the block will not compile), and its declared type is effectively the common supertype, so you can only call members available on that type. Also remember: catch blocks may never catch a checked exception that the try body cannot throw, and finally is optional but at least one of catch/finally must follow the try.