Why can't overriding methods throw exceptions broader than the overridden method?

前端 未结 15 1359
梦谈多话
梦谈多话 2020-11-22 13:47

I was going through SCJP 6 book by Kathe sierra and came across this explanations of throwing exceptions in overridden method. I quite didn\'t get it. Can any one explain it

相关标签:
15条回答
  • 2020-11-22 14:37

    The overriding method must NOT throw checked exceptions that are new or broader than those declared by the overridden method.

    This simply means when you override an existing method, the exception that this overloaded method throws should be either the same exception which the original method throws or any of its subclasses.

    Note that checking whether all checked exceptions are handled is done at compile time and not at runtime. So at compile time itself, the Java compiler checks the type of exception the overridden method is throwing. Since which overridden method will be executed can be decided only at runtime, we cannot know what kind of Exception we have to catch.


    Example

    Let's say we have class A and its subclass B. A has method m1 and class B has overridden this method (lets call it m2 to avoid confusion..). Now let's say m1 throws E1, and m2 throws E2, which is E1's superclass. Now we write the following piece of code:

    A myAObj = new B();
    myAObj.m1();
    

    Note that m1 is nothing but a call to m2 (again, method signatures are same in overloaded methods so do not get confuse with m1 and m2.. they are just to differentiate in this example... they both have same signature). But at compile time, all java compiler does is goes to the reference type (Class A in this case) checks the method if it is present and expects the programmer to handle it. So obviously, you will throw or catch E1. Now, at runtime, if the overloaded method throws E2, which is E1's superclass, then ... well, it's very wrong (for the same reason we cannot say B myBObj = new A()). Hence, Java does not allow it. Unchecked exceptions thrown by the overloaded method must be same, subclasses, or non-existent.

    0 讨论(0)
  • 2020-11-22 14:39

    I provide this answer here to the old question since no answers tell the fact that the overriding method can throw nothing here's again what the overriding method can throw:

    1) throw the same exception

    public static class A 
    {
        public void m1()
           throws IOException
        {
            System.out.println("A m1");
        }
    
    }
    
    public static class B 
        extends A
    {
        @Override
        public void m1()
            throws IOException
        {
            System.out.println("B m1");
        }
    }
    

    2) throw subclass of the overriden method's thrown exception

    public static class A 
    {
        public void m2()
           throws Exception
        {
            System.out.println("A m2");
        }
    
    }
    
    public static class B 
        extends A
    {
        @Override
        public void m2()
            throws IOException
        {
            System.out.println("B m2");
        }
    }
    

    3) throw nothing.

    public static class A 
    {   
        public void m3()
           throws IOException
        {
            System.out.println("A m3");
        }
    }
    
    public static class B 
        extends A
    {   
        @Override
        public void m3()
            //throws NOTHING
        {
            System.out.println("B m3");
        }
    }
    

    4) Having RuntimeExceptions in throws is not required.

    There can be RuntimeExceptions in throws or not, compiler won't complain about it. RuntimeExceptions are not checked exceptions. Only checked exceptions are required to appear in throws if not catched.

    0 讨论(0)
  • 2020-11-22 14:40

    To illustrate this, consider:

    public interface FileOperation {
      void perform(File file) throws FileNotFoundException;
    }
    
    public class OpenOnly implements FileOperation {
      void perform(File file) throws FileNotFoundException {
        FileReader r = new FileReader(file);
      }
    }
    

    Suppose you then write:

    public class OpenClose implements FileOperation {
      void perform(File file) throws FileNotFoundException {
        FileReader r = new FileReader(file);
        r.close();
      }
    }
    

    This will give you a compilation error, because r.close() throws an IOException, which is broader than FileNotFoundException.

    To fix this, if you write:

    public class OpenClose implements FileOperation {
      void perform(File file) throws IOException {
        FileReader r = new FileReader(file);
        r.close();
      }
    }
    

    You will get a different compilation error, because you are implementing the perform(...) operation, but throwing an exception not included in the interface's definition of the method.

    Why is this important? Well a consumer of the interface may have:

    FileOperation op = ...;
    try {
      op.perform(file);
    }
    catch (FileNotFoundException x) {
      log(...);
    }
    

    If the IOException were allowed to be thrown, the client's code is nolonger correct.

    Note that you can avoid this sort of issue if you use unchecked exceptions. (I am not suggesting you do or don't, that is a philosophical issue)

    0 讨论(0)
  • 2020-11-22 14:41

    It means that if a method declares to throw a given exception, the overriding method in a subclass can only declare to throw that exception or its subclass. For example:

    class A {
       public void foo() throws IOException {..}
    }
    
    class B extends A {
       @Override
       public void foo() throws SocketException {..} // allowed
    
       @Override
       public void foo() throws SQLException {..} // NOT allowed
    }
    

    SocketException extends IOException, but SQLException does not.

    This is because of polymorphism:

    A a = new B();
    try {
        a.foo();
    } catch (IOException ex) {
        // forced to catch this by the compiler
    }
    

    If B had decided to throw SQLException, then the compiler could not force you to catch it, because you are referring to the instance of B by its superclass - A. On the other hand, any subclass of IOException will be handled by clauses (catch or throws) that handle IOException

    The rule that you need to be able to refer to objects by their superclass is the Liskov Substitution Principle.

    Since unchecked exceptions can be thrown anywhere then they are not subject to this rule. You can add an unchecked exception to the throws clause as a form of documentation if you want, but the compiler doesn't enforce anything about it.

    0 讨论(0)
  • 2020-11-22 14:42

    Java is giving you the choice to restrict exceptions in the parent class, because it's assuming the client will restrict what is caught. IMHO you should essentially never use this "feature", because your clients may need flexibility down the road.

    Java is an old language that is poorly designed. Modern languages don't have such restrictions. The easiest way around this flaw is make your base class throw Exception always. Clients can throw more specific Exceptions but make your base classes really broad.

    0 讨论(0)
  • 2020-11-22 14:43

    The overriding method CAN throw any unchecked (runtime) exception, regardless of whether the overridden method declares the exception

    Example:

    class Super {
        public void test() {
            System.out.println("Super.test()");
        }
    }
    
    class Sub extends Super {
        @Override
        public void test() throws IndexOutOfBoundsException {
            // Method can throw any Unchecked Exception
            System.out.println("Sub.test()");
        }
    }
    
    class Sub2 extends Sub {
        @Override
        public void test() throws ArrayIndexOutOfBoundsException {
            // Any Unchecked Exception
            System.out.println("Sub2.test()");
        }
    }
    
    class Sub3 extends Sub2 {
        @Override
        public void test() {
            // Any Unchecked Exception or no exception
            System.out.println("Sub3.test()");
        }
    }
    
    class Sub4 extends Sub2 {
        @Override
        public void test() throws AssertionError {
            // Unchecked Exception IS-A RuntimeException or IS-A Error
            System.out.println("Sub4.test()");
        }
    }
    
    0 讨论(0)
提交回复
热议问题