简体   繁体   中英

Exceptions propagate up Call Stack

I'm having a hard time quite understanding the concept, or really the usefulness, of having Exceptions propagate up the call stack. I get how to create them, but I don't really see when they would be used, like in a simple real world application for math or something.

public void method1(){
    try{
        method2();
    }
    catch(Exception e){
        e.printStackTrace();
    }
}

public void method2(){
      try{
          throw new Exception();
      }
      finally{
         System.out.println("no exception, try cleanup");
      }
}

I get that this is basically how it would work, though it would probably be more involved with more exceptions and functions, but I don't really get the point of using these instead of just having catches in every function.

... but I don't really get the point of using these instead of just having catches in every function.

The point is that next function up the call stack may not know how to handle the exception. Example:

public class Test {

    public Object doSomething(String source) throws IOException {
        try (InputStream is = openAsStream(source)) {
            // ... read and process stuff
            return ...
        }
    }

    public InputStream openAsStream(String name) throws IOException {
        String fileName = // ... do something with 'name'
        return new FileInputStream(name);
    }

    public static void main(String[] args) {
        // ...
        Test t = new Test();
        try {
            t.doSomething(args[0]);
        } catch (IOException ex) {
            System.err.println("Cannot handle '" + args[0] + "'");
        }
    }
}

The openAsStream calls the FileInputStream constructor which may throw an IOException . The openAsStream method cannot recover from this so it lets it propagate. The doSomething method doesn't know how to deal with it either, so it allows it to propagate. Finally, the exception gets to main ... which knows how to explain the problem to the user.


Now you could write openAsStream to catch the IOException , print an error message and return a null . But that would be a big mistake:

  • The openAsStream() doesn't (and shouldn't ) know whether / how to report the problem to the user.

  • And if it returns a null to the caller, then the caller has to test to see whether the result of the call is null ... and take alternative action.

The point is that a method should only handle exceptions that can be adequately dealt with at that level. Others should be allowed to propagate. (Or maybe wrapped in another exception ... if that is what the API design requires.)

Sometimes the code that generates the exception does not know how to properly deal with it. If you are in a piece of transaction code, and something blows up, that method / component might not be able to do more than simply log the exception if it tried to handle it. One or more layers up, on the other hand, you may be able to attempt to re-establish a connection, or provide a detailed error response to a requestor.

Often the caller has the proper context to deal with the problem where as the code performing the action that generates the exception does not. Let's say that I am a high level program that wants to write some data out to a file. The low level service that I'm calling, let's call it writeFile() can throw an IOException for a variety of reasons.

The person who wrote writeFile() is going to have no idea of the context in which writeFile() is going to be used. If writeFile() fails, should it attempt to re-write the file? How many times should it try that? Should it just give up? Because the programmer who wrote the low level function writeFile()'s context in the scheme of accomplishing some task is so far down in the weeds the programmer could not possibly anticipate how the caller would want to handle an error condition.

Instead of trying to guess at how the caller would like to have the error handled (an impossible task) the programmer of writeFile() indicates to the client calling writeFile() that there are some "open questions" that need to be answered when problems arise. Each problem is indicated by an exception Class and when the client programmer catches that exception, the client programmer is writing an answer to that open question with the context that the client programmer could never hope to have.

In short, if you EVER see a method listing a checked exception, the programmer who wrote that exception is saying "Whomever calls this method will have the proper context to make a decision about what to do with that exceptional condition. I do not."

Exception propagation makes your code fail-fast on errors by default.

Consider the alternative approach to exception propagation - returning an error code. If the caller to your code, accidentally or deliberately, does not test for error codes, then they could use your method, not be aware that your object is now in an unusuable state, and continue calling methods and causing undefined behaviour/memory corruption. If you threw an exception instead, then if the caller forgets to catch exceptions then instead of doing horrible things they fail fast, and the programmer can be alerted as to where it was thrown, why and what to do about it. Exceptions are loud and obnoxious because they indicate conditions that need to be considered.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM