skumar
skumar

Reputation: 1015

Throws statement doesn't consider inherited exception

I have 2 exceptions:

class MyException1 extends Exception {
 --
}

class MyException2 extends MyException1 {
--
}


function invokeValidation() throws MyException2 {

    obj1.method() // it throws MyException1
    obj2.method() // it throws MyException2
}

The above code says unhandled exception type MyException1, even though MyException2 extends MyException1. Why does it expect MyException1 in the throws statement?

Upvotes: 1

Views: 577

Answers (3)

afi
afi

Reputation: 578

You always throw the Exception and its subclasses, not the other way around (if that was the case, you would always throw Throwable).

Imagine MyException2 having a variable:

String name = "whatever";

Now if you throw an instance of MyException1, but somewhere else catch MyException2, it would go wrong because you cannot type cast from the more special class to its superclass. The field name is not present in MyException1.

The other way around will always work because MyException2 inherits all fields from MyException1.

Upvotes: 1

RealSkeptic
RealSkeptic

Reputation: 34638

I think it may become clearer to you if you use more meaningful exception names, as your exception naming doesn't reflect the real purpose of the exception.

For example, suppose your Exception1 is now IOException. It indicates that there is some input-output problem. And your extending exception, instead of Exception2 is now called FileNotFoundException. It indicates a specific input-output error - that a file was not found.

So, as you see, a FileNotFoundException is a kind of IOException. That's the relationship the word extends defines.

Now imagine if your method was declared:

public void invokeValidation() throws FileNotFoundException {

    obj1.method(); // it throws IOException
    obj2.method(); // it throws FileNotfoundException
}

So, you said "my method throws a FileNotFoundException". The compiler knows that if it runs into this particular Exception, it should throw it up to to the caller. But obj1 may be throwing an IOException. It could be an exception that indicates that there is no space on disk. Or that there is a bad block. Or that the file was closed abruptly. It could be lots of things. An IOException is not a FileNotFoundException. Therefore, the compiler doesn't have anything to do with it, and it tells you that this is an unhandled exception.

But if your method was declared:

public void invokeValidation() throws IOException {

    obj1.method(); // it throws IOException
    obj2.method(); // it throws FileNotfoundException
}

The compiler would not complain. Why? Because for obj1, it throws an IOException, and that's fine, it's right there in the title, it can be thrown one level up. And for obj2, it knows that FileNotFoundException is a (type of) IOException, and therefore it is allowed to throw it. It is polymorphic with IOException, the caller will know what to do with it.

So the hierarchy of inheritance is important. If you have two methods that throw two exceptions, and one is a superclass of the other, you should declare your method to throw the superclass. Because any subclass has an "is_A" relationship with the superclass. It is a specific kind of the superclass. But the reverse is not true.

Upvotes: 2

Eran
Eran

Reputation: 394126

If obj1.method() throws a MyException1, it may throw a sub-class of MyException1 that is not MyException2 (or a sub-class of MyException2). Therefore it's not enough to declare that invokeValidation() throws MyException2.

On the other hand, if you declare invokeValidation() throws MyException1, that would be sufficient, since any MyException2 exception is also a MyException1.

Upvotes: 4

Related Questions