Search code examples
javaexceptionioexceptioncheckedthrows

Declaring checked exception using throws clause that is never thrown in the body of the method


I understand that catching a checked exception that is never thrown in the corresponding try block is invalid. Because the compiler itself would have forced the programmer to handle the exception if it was to occur.

For example, this code snippet -

try
{
}
catch(IOException e)
{
}

is invalid.

But why doesn't the compiler work the same way for the method that throws a checked exception that has never been thrown in the body of the method?

For example, this code snippet -

void test() throws IOException
{
}

is surprisingly valid.

Please explain the reason behind it. TIA.


Solution

  • It is a compile-time error if a catch clause can catch checked exception class E1 and it is not the case that the try block corresponding to the catch clause can throw a checked exception class that is a subclass or superclass of E1, unless E1 is Exception or a superclass of Exception.

    This tells you all these are valid:

    try { }
    catch(Exception e){}
    

    --

    try{ }
    catch(NullPointerException  e) {}
    

    --

    try{ }
    catch(ArrayIndexOutOfBoundsException  e) {}
    

    --

    try{ }
    catch(RuntimeException e) {}
    

    --

    try{ }
    catch(Error e) {}
    

    --

    try{ }
    catch(Throwable e){ }
    

    It's not related to a void block. It's related to an impossible path to your checked subclass of Exception.


    What happens with subclasses of Exception? For example IOexception: Those are unreachable catch blocks. Nothing in the try block can lead to that exception, so the compiler just tells you: this block would never be executed, as it would never catch that subException.

    The difference with throws: The concept of unreachable code doesn't exist in this context. The possibility of a method throwing an exception doesn't end in the deffinition of the method. For example:

    abstract void readFile(String path) throws IOException;
    

    This method doesn't even have a block, as it's an abstract method. It's easy to guess that this line won't ever throw any IOException. But it defines a behaviour for the extensions that will implement it. In order to override it, your method must throw the IOException.

    In the same way, if someone overrides your test method:

    @Override
    void test() throws IOException
    {
       readFile(file);
    }
    

    It's not impossible to happen, in contrary to your first try-catch block.