eh3rrera / ocpj8-book

Study guide for the Oracle Certified Professional, Java SE 8 Programmer Exam (1Z0-809)
Other
129 stars 90 forks source link

Chapter 19 - definition of checked exceptions #37

Closed ivan-moto closed 7 years ago

ivan-moto commented 7 years ago

Stated twice: "Exception and its subclasses (except for RuntimeException) are known as checked exceptions because they have to be caught at some point by a try-catch statement."

Checked exceptions are called checked exceptions because they're checked at compile time.

About multi catch statements: "One last rule. You can only combine exceptions in a multi-catch block that are not directly related (regarding inheritance)"

I don't think this is true. I've written code with exceptions that both extended from IOException and had no issues. Please verify this statement.

"This is similar to the case when a superclass is declared in a catch block before the subclass. The code is redundant, the superclass will always catch the exception. Only use the multi-catch block for exceptions that are not related."

It's actually not always redundant. For example if I write

catch ( ZipException | SSLException e ) {...
catch (IOException e) {

The first 2 exceptions both extend from IOException but I'm explicitly saying I only want to catch these 2 and do something with them. I think it's also worth pointing out in your book that in the first catch block e will end up being the closest common parent of ZipException and SSLException... it won't actually be one or the other.

eh3rrera commented 7 years ago

Hi Ivan,

Great observations.

What do you think if we change:

Exception and its subclasses (except for RuntimeException) are known as checked exceptions because they have to be caught at some point by a try-catch statement.

To this:

Exception and its subclasses (except for RuntimeException) are known as checked exceptions because the compiler has to check they are caught at some point by a try-catch statement.

About multi catch statement, you're right. I was referring to a parent-child relationship since I don't consider siblings (like ZipException and SSLException) to be directly related through inheritance. To make it clearer, what if we change:

One last rule. You can only combine exceptions in a multi-catch block that are not directly related (regarding inheritance)

To:

One last rule. You cannot combine subclasses and their superclasses in the same multi-catch block.

About the last point, I was referring that this is redundant (not the case you put as an example):

catch (IOException e) { ... }
catch (ZipException e) { ...  }

The catch block that catches ZipExceptionis never going to be executed because the IOExceptionwill always catch ZipExceptions. What if we just removed the last phrase to leave the paragraph like this:

This is similar to the case when a superclass is declared in a catch block before the subclass. The code is redundant, the superclass will always catch the exception.

About your last point, I don't think I quite get it:

I think it's also worth pointing out in your book that in the first catch block e will end up being the closest common parent of ZipException and SSLException... it won't actually be one or the other.

How come it won't actually be one or the other? If a ZipExceptionor a SSLExceptionis thrown (or one of its subclasses), it will be caught by the multi-catch block, and we can check with the instanceofoperator if it's either a ZipExceptionor a SSLException.

ivan-moto commented 7 years ago

Looks great. Thanks for taking my feedback.

I see now what you are saying about unreachable catch blocks. Your example code is:

} catch (ArithmeticException | RuntimeException e) {

So it doesn't make sense to have them both because ArithmeticException descends from RuntimeException. I think it would help to explicitly point that out. I didn't catch that one descends from the other until just now.

About the last point, what I'm saying is that if that first catch block is called the exception object passed along will be the closest common parent of all the exceptions in that block, not the specific exception that was actually caught.

See: https://stackoverflow.com/a/17635622/4913489

I first read about this in Learning Java 4th Edition

In Java 7, there is an alternative to using multiple catch clauses, and that is to handle multiple discrete exception types in a single catch clause using the “|” or syntax:

try {
// read from network... // write to file..
catch ( ZipException | SSLException e ) {
logException( e );
}

Using this “|” or syntax, we receive both types of exception in the same catch clause. So, what is the actual type of the e variable that we are passing to our log method? (What can we do with it?) In this case, it will be neither ZipException nor SSLException but IOException, which is the two exceptions’ nearest common ancestor (the closest parent class type to which they are both assignable). In many cases, the nearest common type among the two or more argument exception types may simply be Exception, the parent of all exception types. The difference between catching these discrete exception types with a multiple-type catch clause and simply catching the common parent exception type is that we are limiting our catch to only these specifically enumerated exception types and we will not catch all the other IOException types, as would be the alternative in this case.

I tried to find some documentation from Oracle on this but couldn't. It might be worth testing to see which exception is actually thrown.

I'm new to all of this so thanks for your great book.

eh3rrera commented 7 years ago

I don't agree with what the book and the SO answer say.

According to the Java Language Specification (at the end of that section):

A multi-catch clause can be thought of as a sequence of uni-catch clauses. That is, a catch clause where the type of the exception parameter is denoted as a union D1|D2|...|Dn is equivalent to a sequence of n catch clauses where the types of the exception parameters are class types D1, D2, ..., Dn respectively.

It doesn't say that the type of the exception is the nearest common ancestor.

If you execute:

try {
  if(true) throw new ZipException();
  else throw new SSLException("");
} catch ( ZipException | SSLException e ) {
  System.out.println(e.getClass().getName());
}

The output will be:

java.util.zip.ZipException

If SSLExceptionis thrown, the output is:

javax.net.ssl.SSLException

Of course, if you execute something like this:

try {
  if(true) throw new ZipException();
  else throw new SSLException("");
} catch ( ZipException | SSLException e ) {
  System.out.println(e instanceof java.io.IOException);
}

The output will be truebecause a ZipException (as a subclass of IOException) is an IOException, but the type of e is not really IOException.

ivan-moto commented 7 years ago

That's great. Thanks for clearing that up.

ivan-moto commented 7 years ago

Feel free to close this when the changes are made.

eh3rrera commented 7 years ago

Done, thanks!