8

Research following method:

static private void foo()  {
        try {
            throw new FileNotFoundException();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

This code compiles good despite last catch block actually unreachable.

Now lets comment throw new FileNotFoundException(); row

execute:

OOOPs! we see

Unreachable catch block for FileNotFoundException. This exception is never thrown from the try statement body

Strange. Why does java use double standards for these situatons?

update for @Peter Rader

static private void foo(FileNotFoundException f)  {
        try {
            throw f;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

work as well as with constructor invocation

update

I noticed that on different versions of java compiler I see different result of compiling this code.

public class RethowTest {

        public static void main(String[] args)  {
            try {
                throw new FileNotFoundException();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                throw e;
            }
        }    
}

on my local pc: java 1.7.0_45 -

C:\Program Files\Java\jdk1.7.0_45\bin>javac D:\DNN-Project\DNN-Project\src\main\java\exceptionsAndAssertions\RethowTest.java
D:\DNN-Project\DNN-Project\src\main\java\exceptionsAndAssertions\RethowTest.java:15: warning: unreachable catch clause
                } catch (IOException e) {
                  ^
  thrown type FileNotFoundException has already been caught
1 warning

java 1.6.0_38

D:\DNN-Project\DNN-Project\src\main\java\exceptionsAndAssertions\RethowTest.java:16: unreported exception java.io.IOException; must be caught or declared to be thrown
                    throw e;
                    ^
1 error

http://www.compileonline.com/compile_java_online.php (Javac 1.7.0_09) -

HelloWorld.java:9: warning: unreachable catch clause
        } catch (IOException e) {
          ^
  thrown type FileNotFoundException has already been caught
1 warning
gstackoverflow
  • 36,709
  • 117
  • 359
  • 710
  • 9
    Exception includes runtime exceptions. It's never unreachable in principle. A catch for FileNotFoundException is only reachable if something in the try block throws it, or one of its base classes. – user207421 Sep 02 '14 at 13:16
  • 1
    @EJP For gods sake, can you please add it as an answer ? – Suresh Atta Sep 02 '14 at 13:20
  • 1
    @sᴜʀᴇsʜ ᴀᴛᴛᴀ EJP comment is not answer - it is just observation to improve question – gstackoverflow Sep 02 '14 at 13:21
  • 1
    The only not very satisfying excuse is that FileNotFoundException is an IOException too, and the catch only seems to check, that an IOException is thrown, not considering it being already catched. – Joop Eggen Sep 02 '14 at 13:25
  • @Joop Eggen interesting opinion – gstackoverflow Sep 02 '14 at 13:27
  • @gstackoverflow If FileNotFoundException would have been catched after catching IOException the compiler gives an error, so my guess is that not all use cases were considered. And one has to admit that unreachable-code errors are not functional errors causing the application to fail. – Joop Eggen Sep 02 '14 at 13:57
  • I suspect that reason may be similar to why `if(flase){dead code}` is allowed when `while(false){dead code}` is not - it can help in simple debugging. – Pshemo Sep 02 '14 at 15:10
  • @Pshemo didn't know about while(false). And what the reason? – gstackoverflow Sep 02 '14 at 18:22
  • @gstackoverflow It is only a guess which I read somewhere (not on official forum), but here I go: `if(flase){dead code}` can help in simple debugging (for instance performed by novice programmers) so they could write `if (false){..} if(true){..} if(false){..} if(true){..}` and by change some `false` to `true` they could test few combinations of some scenarios. But `while(false)` is not that useful because even if we change `false` to `true` we will end up in infinite loop, so we will not to be able to leave and test other combinations. I am not the author of this idea but it seems reasonable. – Pshemo Sep 02 '14 at 18:36
  • @gstackoverflow [This answer](http://stackoverflow.com/a/20299947/1393766) seems to agree with this kind of rationale, also if [T.J. Crowder agrees](http://stackoverflow.com/questions/20299914/iffalse-vs-whilefalse-unreachable-code-vs-dead-code#comment30289450_20299947) then I believe him. – Pshemo Sep 02 '14 at 18:38
  • @gstackoverflow From [JLS 14.21](http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.21) *the rationale for this differing treatment is to allow programmers to define "flag variables" such as: `static final boolean DEBUG = false;` and then write code such as: `if (DEBUG) { x=3; }`. So maybe for similar reasons, we compiler allows adding code for supertype exceptions even if all exceptions ware handled (for example to do `catch(Exception e){//some kind of logging when we are sure exceptions shouldn't happen in tested case}`). – Pshemo Sep 02 '14 at 18:46
  • I have read this answer but I didn't understand. Can you answer in details in answer(not in comment) – gstackoverflow Sep 02 '14 at 18:58
  • This is only a guess/suspicion so it is better suited as comment. Also it would be more about `if(false)` vs `while(false)` rather than `catch(SuperTypeOfHandledException e)`. – Pshemo Sep 02 '14 at 19:01
  • I didn't understand connection between: 1. my question 2 debug 3 C language 4 binary backward compatibility – gstackoverflow Sep 02 '14 at 19:15
  • @gstackoverflow If `f` is `null`, the constructor of `NullPointerException` is called and the Constructor can throw a `IOException` in `fillInStackTrace`. See https://community.oracle.com/thread/1445008?start=0 – Grim Sep 03 '14 at 02:21
  • Simple question: Why do you care? The Java compiler does what it wants, and effectively defines the language. The behavior you've described does not create a functional limitation, so why worry about it? – Hot Licks Sep 03 '14 at 02:41

3 Answers3

5

The reachability rules are defined in the Java 8 JLS 14.21 (and Java 7) as follows:

A catch block C is reachable iff both of the following are true:

  • Either the type of C's parameter is an unchecked exception type or Exception or a superclass of Exception, or some expression or throw statement in the try block is reachable and can throw a checked exception whose type is assignable to the type of C's parameter. (An expression is reachable iff the innermost statement containing it is reachable.)

    See §15.6 for normal and abrupt completion of expressions.

  • There is no earlier catch block A in the try statement such that the type of C's parameter is the same as or a subclass of the type of A's parameter.

Note that the rules DO NOT forbid your example code. The second catch block does not meet the criteria of the second bullet point.

(In the original version of the example, you caught Exception. The reachability reasoning would be different, but the answer is the same - valid code.)

Is this inconsistent? For your example, you could argue that is the case.

Why didn't they address this case in the reachability rules? I don't know. You'd need to ask the Java designers!! However:

  • The formulation of the reachability rules would need to be significantly more complicated to handle this. Extra (unnecessary?) complexity in a specification is a concern.

  • You could argue that this inconsistency doesn't break anything. The reachability rules are really just a way of picking up potential errors in the users code. It doesn't involve type-safety or predictable execution; i.e. stuff that would "break" Java runtime semantics.

  • If they changed the spec now, that would render invalid a small proportion of valid and working Java programs. That's not a good idea, given that stability is one of the main selling points of Java.

On the other hand, I cannot think of a technical reason why they couldn't have addressed this "inconsistency" in the spec.


You noted that some Java compilers give a Warning message on the 2nd catch. That is OK. A Java compiler is allowed to give warnings for things that are (technically) legal Java code.

If they were Errors, that would technically be a compiler bug ... according to my reading of the JLS.

Community
  • 1
  • 1
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • 1
    **The second catch block does not meet the criteria of the second bullet point.** can you clarify?why not? – gstackoverflow Sep 03 '14 at 06:35
  • 2
    @gstackoverflow The second bullet point means you can't catch `IOException` _followed by_ `FileNotFoundException` because `FileNotFoundException` is a subclass of `IOException`. Your example has the opposite order, so that point doesn't apply. – fgb Sep 03 '14 at 13:08
1

The catch (Exception ...) block will catch runtime exceptions. It's never unreachable in principle.

FileNotFoundException is a checked exception. A catch block for it is only reachable if something in the try block throws it, or one of its child classes.

[in response to requests]

gstackoverflow
  • 36,709
  • 117
  • 359
  • 710
user207421
  • 305,947
  • 44
  • 307
  • 483
-3

If you instanciate new FileNotFoundException() you call the constuctor of the Class FileNotFoundException. In this constructor a IOException can theroretically be thrown by calling the native method fillInStackTrace - the compiler may not know whats the content of the Constructor, maybe a IOException will be thrown.

See this article: https://community.oracle.com/thread/1445008?start=0 in example.

If the compiler looks into the constructor FileNotFoundException() for every occourcence: its a overhead java neglect for performance.

Grim
  • 1,938
  • 10
  • 56
  • 123
  • +1 you are right. Now its weird - no doubt. But f may be null, a Null-Pointer exception will be thrown, noone knows if NullPointerException not inheried a IOException. – Grim Sep 02 '14 at 13:37
  • 2
    I don't understan your idea. Null-Pointer is RuntimeException annyway. You shouldn't check these exceptions. – gstackoverflow Sep 02 '14 at 13:51
  • 1. Dont think classes and their inheritations do not change! Even the compiler do not think inheritations change! 2. `NumberFormatException` is a `RuntimeException` too, you realy should check these exceptions! – Grim Sep 02 '14 at 17:34
  • @PeterRader This is complete nonsense from start to finish. (1) `new FileNotFoundException()` does not throw any kind of `IOException.` (2) `FileNotFoundException` and `NullPointerException` are not in an inheritance relationship in either direction. (3) The compiler knows what every constructor and method throws, without 'looking into the constructor': it's in the object code. (4) Java checks all checked exceptions: there is no 'neglect for performance'. – user207421 Sep 02 '14 at 23:50
  • @EJP 1. The compiler knows only the objectcode of the current JRE/JDK-Version. In this Bug in example the native method `fillInStackTrace` throws a `IOException` who is called in the constructor of `RuntimeException` (the `FileNotFoundException` also uses) uses: https://community.oracle.com/thread/1445008?start=0 . 2. I can use dynamic proxys to inherit from `NullPointerException` as well as `FileNotFoundException`. (3) The compiler knows only what every constructor or method declare in their method/constructor signature. A interface-method do not have object-code, that does mean the method th – Grim Sep 03 '14 at 02:06
  • Sorry take too much lines for discussion. If you have doubts about your point, open up a chat and let me aregue you about my point. – Grim Sep 03 '14 at 02:12
  • @PeterRader 1. The compiler knows enough to check checked exceptions, and it knows that constructing a FileNotFoundException doesn't throw an IOException, which is more than you seem to. 2. You cannot use dynamic proxies to inherit from anything, only to implement interfaces. 3. The question of what may change in future JREs isn't the compiler's concern, but nobody is going to change the JRE in the respect you suggest. You're just confusing the issue. You haven't even answered the question properly. If everything you've said was true the compiler wouldn't be able to check exceptions at all. – user207421 Sep 03 '14 at 02:54
  • @PeterRader The link you provide is not an example of an IOException being thrown. It is an example of a RuntimeException being thrown. Look again. – user207421 Sep 03 '14 at 03:01
  • @EJP no, the IOException is thrown and wrapped and thrown into a RuntimeException by the container (BEA WebLogic). A real IOException is thrown! – Grim Sep 03 '14 at 09:50