Bob Balaban's Blog

     
    alt

    Bob Balaban

     

    The Interesting Difference Between "Exception" and "RuntimeException" (Java)

    Bob Balaban  February 2 2013 06:43:32 PM
    Greetings Geeks!

    I hope everyone who attended Lotusphere IBMConnect '13 had a good time and has returned home safely. I didn't attend (again, I stopped going after the first 17), but I've been hearing that it was pretty good.

    I learned something new about certain types of exceptions in Java programming the other day. Here's the context:
    • I had an Enum class with a few items in it
    • I had a String that came from outside the program that I wanted to convert into an enum'ed constant
    • This was happening inside an existing function where I didn't want to add to the "throws" declaration (because that would cause ripple effects up into the functions that called the function I was modifying)

    Here's my first draft implementation (this isn't the real code, I created a standalone example for ya):

    import
    java.io.*;

    public
    class ExceptionTest
    {
           public enum someEnum
           {
                   firstitem, seconditem, thirditem
           }
           
           public static void main(String [] args)
           {
                   try { func1(args[0]); }
                   catch (IOException ioe) {}
                   
                   try { func2(args[0]); }
                   catch (IOException ioe) {}
           }
           
           public static void func1(String arg) throws IOException
           {
                   someEnum myEnum = null;
                   
                   System.out.println("In func1");
                   if (arg.equals("firstitem"))
                           myEnum = someEnum.firstitem;
                   else if (arg.equals("seconditem"))
                           myEnum = someEnum.seconditem;
                   else if (arg.equals("thirtitem"))
                           myEnum = someEnum.thirditem;
                   else System.out.println("Invalid input: " + arg);
                   
                   if (myEnum != null)
                           System.out.println("myEnum: " + myEnum.name());
           }
    }

    Not too bad, but rather tedious.  Note that myEnum.name() will take the enum's item value and convert it into a String.

    A colleague pointed out to me that the Java Enum class (all "enums" are compiled into instances of the class Enum) has a valueOf() method. You give it a string, it returns the enum item (e.g., someEnum..firstitem). So, it's effectively the reverse of the name() method.

    I noticed from the javadoc that this function can throw an IllegalArgumentException. It'll do that if the string you give it doesn't match one of the enum items. So I changed my "func1" implementation into what I'll show you here as func2():

           public static void func2(String arg) throws IOException
           {
                   someEnum myEnum = null;
                   
                   System.out.println("In func2");
                   try { myEnum = someEnum.valueOf(arg); }
                   catch (IllegalArgumentException iae) {}
                   
                   if (myEnum == null)
                           System.out.println("Invalid input: " + arg);
                   else System.out.println("myEnum: " + myEnum.name());
                   
           }

    Simpler, right? I thought at this point that I had it nailed. But then that same colleague said, "Hey, you know you don't need the try/catch there". I was sure he was wrong (this stuff happens to me all the time). "No, I do need it, because I don't want to add IllegalArgumentException to the function 'throws' list." I was sure that if I took out the try/catch I'd get a compile error saying I needed it.

    Buuuuuuutt noooooooo, I was wrong. And here's what I hadn't realized until then: There are TWO categories of Exceptions. Many classes that extend (inherit from) java.lang.Exception (like IOException, for example), are required to either be declared in a "throws" clause in the function declaration, or "caught" in a try/catch block. The compiler enforces this.

    However, SOME exception classes (such as IllegalArgumentException) extend not java.lang.Exception, but java.lang.RuntimeException (which itself extends Exception). These are special, you don't need to catch them or declare them explicitly. Here's the slimmed down version of func2():

           public static void func2(String arg) throws IOException
           {
                   someEnum myEnum = null;
                   
                   System.out.println("In func2");
                   myEnum = someEnum.valueOf(arg);
                   
                   if (myEnum == null)
                           System.out.println("Invalid input: " + arg);
                   else System.out.println("myEnum: " + myEnum.name());
                   
           }

    No compile problems. AND, if you later add more items to the enum, you don't have to change any of the other code.

    If you run this with a valid input (such as "firstitem"). If you run it with something other than a string that matches one of the enum items, you get this:

    Exception in thread "main" java.lang.IllegalArgumentException: No enum constant ExceptionTest.someEnum.item
           at java.lang.Enum.valueOf(Unknown Source)
           at ExceptionTest$someEnum.valueOf(ExceptionTest.java:1)
           at ExceptionTest.func2(ExceptionTest.java:42)
           at ExceptionTest.main(ExceptionTest.java:15)

    Of course, you still CAN use try/catch here. You might do that if you want to catch the "illegal" argument and re-throw an application-specific Exception, or create a nicer error message. But you don't have to, because derivatives of RuntimeException are treated specially by the Java compiler.

    Now you know!

    Geek ya later!

    Follow me on Twitter @LooseleafLLC
    This article ┬ęCopyright 2013 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.