Thursday, January 26, 2017

Slapping in the face of checked vs unchecked exceptions confusion

I've come across lots of people finding it damn difficult to understand the difference and purpose of checked and unchecked exceptions in Java. In fact, I used to feel the same about this. So I decided to contribute my two cents in hopes it helps anyone. 

To make it easy to understand,I'll first talk very briefly about "catch or throw" rule that we have for exceptions in Java. In fact, this rule and the concept of checked and unchecked exceptions are so mutually dependent, that it is a little hard to decide which one to explain first. So I picked up this rule, as no matter which one I start with, eventually everything's going to be clear when connecting the ends.

Consider the following method that throws FileNotFoundException when, well when it cannot find the specified file: 




    void printFileContent(String fileName)throws FileNotFoundException,
                                                                IOException{
        FileInputStream fis = new FileInputStream(fileName);
        int read;
        while((read = fis.read() ) != -1)
        {
            System.out.print((char)read);
        }
    }


The programmer that coded the above method did nothing wrong. FileInputStream constructor takes a file name and it tries to build a FileInputStream object based on that file. When it can't find the file, it throws the self descriptive exception called FileNotFoundException. The only thing he/she could've done was to either absorb the exception or throw it to the caller of the printFileContent method. As you might've guessed, absorbing the exception internally without ever letting know the caller method what happened would make no sense, so it's better to throw the exception such that the caller knows that the file does not exist. Because it's the caller who is responsible for providing a valid, existing file name. 

BTW, do you see a redundant piece of code in the above method? The method rethrows 2 exceptions: FileNotFoundException, which is thrown by FileInputStream constructor and IOException, which is thrown by the read method of the FileInputStream class. In fact, we didn't need to specify FileNotFoundException in the throws section, because it's a subclass of IOException. And you probably know that we can specify super class for all child exception types. The same applies for catch. Just core inheritance case.

Now, if I were to neither throw, nor catch the FileNotFoundException, I would've been warned by the compiler, thus the compilation would fail. Why? Because FileNotFoundException as well as the IOException are checked exceptions. This does not mean that there's some kind of attribute attached to them denoting them as checked. No. Why they are called checked is because compiler checks whether the "catch or throw" rule has been applied to them. But how does compiler know which exceptions to check for? Well, it's quite straight forward:

Compiler skips all exceptions that descend from RuntimeException, which therefore are called unchecked exceptions. Repeat with me: All RuntimeExceptions are unchecked exceptions and all unchecked exceptions are RuntimeExceptions. That simple. 

So checked exceptions are the ones that are not RuntimeException. And why they're called checked is because compiler checks the "catch or throw" rule on them. To make final definition on this: This categorization of checked and unchecked is in terms of compiler check. 

But still you might be confused by this thought: FileNotFoundException is also thrown in runtime anyway. And in fact any exception is thrown during runtime. There's no exception that can be thrown without running the container program. Why are exceptions like NullPointerException, ArrayIndexOutOfBoundsException,ArithmeticException subclasses of RuntimException when IOException is not? Well, I might agree that the roof name RuntimeException was not the best one to choose. To answer this, let's assume that there's no exception class called RuntimeException and James Gosling, Mike Sheridan and Patrick Naughton are in the middle of the process of designing the Java language. Mike has a great idea to make programmers be aware of possible exceptions. If they're aware, then they will decide what to do with them when they get one. They will either handle it or they will throw it to the caller of their methods. But  they will be aware of them. James finds this idea to be great but he suggests to skip those exceptions that are caused by bad programming. He thinks a programmer should be concerned only about those exceptions that can be caused by natural events, such as when trying to connect to a database and the database is down, or when reading from network socket and suddenly some switch malfunctions, or when trying to open a file that has recently been removed etc. But he/she shouldn't be concerned about the exception that might be thrown when code inside the method he/she calls invokes a method on some object that might not have been properly instantiated. So they agree on this: Let's collect those exceptions that are result of bad programming under a class called, ummm.....What about RuntimeException. Yes, let's call it RuntimeException and make the compiler check all exceptions except those that are RuntimeException.

Now, take a look at the following examples of bad programming practice:



    String trimAnySpace(String str){
        return str.replace(" ","");
    }

This tiny code looks very naive but there's a flaw in it. What if the caller of this method decides to pass null for the str ? That's correct, the NullPointerException. The programmer must first check if the passed parameter is a valid object. Here's one of several possible solutions

    String trimAnySpace(String str){

       if(str!=null)
        return str.replace(" ","");
       return str;

    }
Or you could throw another exception that would let the caller know the passing null argument is not allowed.

Here's another buggy piece of code:
    int divideFirstElementWithDivisor(int[] numbers, int divisor){
        return numbers[0]/divisor;
    }

Please, take a look at this one. Just one line of code can potentially produce 3 different exceptions.

  1. What if the numbers is passed a null value? Answer is NullPointerException.
  2. What if the numbers array is empty? Answer is ArrayIndexOutOfBoundsException.
  3. What if the divisor is passed 0? Answer is ArithmeticException.
So to conclude. Checked and unchecked exception concept was essentially built to help programmers be aware of possible exceptions(again, the natural exceptions). They do nothing different. What you can do with checked ones, you can also do with the unchecked ones and other way around. And unchecked exceptions are in fact the ones that are outside of the set of checked exceptions. There are unchecked exceptions because there are checked exceptions. It's like the movie Terminator. There's no Terminator 1. There is Terminator 2 because there is a movie called Terminator. Hadn't there been Terminator, there wouldn't have been Terminator 2 either. Phew.