Throwables

Errors and Exception

Signaling Problems

The challenge of handling the not-so-happy-path

  • Need to communicate errors
  • Need to react to errors (communicated or not)
  • Communication: Tell a caller that something went wrong
  • React: Deal with errors in a safe manner
  • Distinguish: Figure out what went wrong
public int div(int a, int b)
{
    // how to communicate illegal division by 0?
    return a / b;
}
public Reader open(String path)
{
    // what if the file does not exist
    return new FileReader(new File(path));
}
public Date parse(String date, Locale locale)
{
    // how to tell the caller that the input was illegal?
    // there are three types of illegal:
    // null, not parsable, and not parsable for the locale
}

What could happen

Types of problems we might encounter

  • Unexpected Errors (unlikely): That something can go wrong is a total surprise. Such as disk is full and neither code nor documentation indicate that. Typically we cannot continue after that.
  • Possible Errors (rare): It is possible that this problem occurs aka it is known but happens rarely. Such as cannot read an existing file. Program must handle the problem and can continue.
  • Possible Errors (typical): This problem can occur often and must be dealt with, such as incorrect user input that leads to conversion problems of data for instance.
  • Coding Errors: Avoidable errors during creation of the program but not detectable by a compiler. Typically illegal arguments, null, wrong assumption about size and so on. Handling the problem is not encouraged (test automation to the rescue), solving it is.

Old School Error Handling

The old way of doing things

  • The return value is the message
  • Reserve return values as communication vehicle
  • -1 means something went wrong
  • -2 no idea what went wrong
  • -3 panic
  • 0 all fine
  • Makes caller code complicated
  • Java has only one return value, not multiple, so values and errors are in the same type range
  • What if the range must be fully used?
  • What if we have different errors?
  • What if we want no return value?
  • How to form a contract based when nobody is forced to check it?

Java's Offer

What Java offers to deal with problem types

  • Error handling concept via throw, try, catch, finally statements
  • Throwing an error means starting communication
  • Errors bubble up until someone deals with them
  • Three types of errors: Errors, Exceptions (checked and unchecked)
  • Compiler support to enforce error handling or at least considering it with throws
  • Throwable
    • Interface that permits error communication via throw
  • Error
    • Implementation of Throwable
    • Serious problem that should NOT be handled
  • Exceptions
    • Implementation of Throwable
    • Indicates conditions that a reasonable application might want to catch
    • Checked: Compiler enforces handling considerations
    • Unchecked: Runtime exception without compiler support that can happen at any time, not part of any programming contract

Throw

Raise a Throwable

  • Raise any Throwable a this point in the code
  • Runtime path will change
  • Wide range of ready to use exceptions available
  • Checked, unchecked or error
/**
 * This method raises a runtime exception
 */
public String trouble(final String foo)
{
    if (foo == null)
    {
        // raise without explaination
        throw new NullPointerException();
    }

    if (foo.length() == 0)
    {
        // raise with explaination
        throw new NullPointerException("Empty string given");
    }

    return foo + System.currentTimeMillis();
}
Exception in thread "main" java.lang.NullPointerException
	at Test.trouble(Test.java:12)
	at Test.main(Test.java:26)
Exception in thread "main" java.lang.NullPointerException: Empty string given
    	at Test.trouble(Test.java:12)
    	at Test.main(Test.java:26)

Throws

Declares that an exception has to be dealt with

  • Throws as part of message declaration
  • Tells compiler that exception X can be expected
  • Compiler will enforce that caller handles it
  • Becomes API declaration
/**
 * Our checked(!) exception
 */
public class BarException extends Exception
{
    ...
}

/**
 * I will be called
 */
public void foo() throws BarException
{
    throw new BarException();
}

/**
 * The caller
 */
public void callFoo()
{
    try
    {
        foo();
    }
    catch(final BarException e)
    {
        // compiler enforces this
    }
}

/**
 * The caller version 2
 */
public void callFoo2() throws BarException
{
    foo();
}

Errors

Serious trouble is indicated

  • Serious problem that should NOT be handled
  • Hands off and never catch it, except when you know what you do
  • No declaration in throws needed or recommended
  • All errors extend java.lang.Error
/**
 * Predefined errors
 */
AnnotationFormatError, AssertionError, AWTError, 
CoderMalfunctionError, FactoryConfigurationError,
FactoryConfigurationError, IOError, LinkageError, 
SchemaFactoryConfigurationError, ServiceConfigurationError, 
ThreadDeath, TransformerFactoryConfigurationError, 
VirtualMachineError

Exceptions

Problems you have to and should handle

Checked

  • Extends Exception
  • Usabable in Throws
  • Compiler will demand declaration and handling
  • Use it for runtime state indications

Unchecked

  • Extends RuntimeException
  • Can be part of Throws but compiler does not care
  • Use it for programming error indication

try-catch

Handle an exception

  • Handle the exception
  • Gives the runtime a target to jump to
  • Do what is necessary in catch
  • Another block, another scope!
public void test()
{
    try
    {
        InputStream is = new FileInputStream(new File("foo.txt"));

        // more code that is not executed when the exception is raised
    }
    catch(final FileNotFoundException e)
    {
        // do your stuff here
        // try to avoid other exceptions
    }

    // this code runs for sure
    String a = "hi";
    
}

try-multi-catch I

Deal with multiple exceptions differently

  • Deal with multiple exceptions
  • Deal with each exception differently
public void test()
{
    try
    {
        InputStream is = new FileInputStream(new File("foo.txt"));

        // more code that is not executed when the exception is raised
        throw new IOExcpeption();
    }
    catch (final FileNotFoundException e)
    {
        // do your stuff here
        // try to avoid other exceptions
    }
    catch (final IOException e)
    {
        // do your stuff here
        // try to avoid other exceptions
    }

    // this code runs for sure
    String a = "hi";
    
}

try-multi-catch II

Handle exceptions the same way

  • Deals with multiple exceptions
  • Deals with each exception the same way
  • New since Java 7
  • You cannot mix everything, IOException and FileNotFoundException for instance don't work due to FileNotFoundException being a subclass of IOException.
public void test()
{
    try
    {
        InputStream is = new FileInputStream(new File("foo.txt"));

        // more code that is not executed when the exception is raised
    }
    catch (final FileNotFoundException | final InvalidHandleException e)
    {
        // do your stuff here
        // try to avoid other exceptions
    }    
}

try-finally

Code that should run regardless

  • When code has to run independent of the outcome
  • Will be called by Java independent of Exceptions or Errors
  • Be careful not to cause other errors
  • Perfect for cleanup
public void test()
{
    try
    {
        InputStream is = new FileInputStream(new File("foo.txt"));

        // more code here that might not run
    }
    finally
    {
        // this will always run
    }    
}

try-catch-finally

Catch and clean

  • Prepare for all execution path
  • Handle code execution regardless of try outcome
public void test()
{
    try
    {
        InputStream is = new FileInputStream(new File("foo.txt"));

        // code that is not executed when the exception is raised
    }
    catch (final FileNotFoundException e)
    {
        // do special stuff for the exception case
    }
    finally
    {
        // this will always run
        // don't raise other exceptions
        // this will run last!
    }    
}

try-finally and return

Something special about return

  • Finally will be executed before return despite return being before finally
  • Can be a little confusing and is sometimes overlooked
public void test()
{
    try
    {
        InputStream is = new FileInputStream(new File("foo.txt"));

        return; // I am not the last statement of this execution!
    }
    catch (final FileNotFoundException e)
    {
        // do special stuff for the exception case
    }
    finally
    {
        // this will always run
        // and the execution will happen despite the return
    }    
}

try-with-resource

Less code to deal with

  • When you deal with resources that require closing after use or similar
  • Requires implementation of java.lang.AutoCloseable for the resource in question
// Old School
public void test()
{
    InputStream is = null;
    try
    {
        is = new FileInputStream(new File("foo.txt"));
    }
    catch (final FileNotFoundException e)
    {
        // do special stuff for the exception case
    }
    finally   
    {
        if (is != null)
        {
            is.close(); // can raise an exception!
        }
    }    
}
// The New Kid
public void test()
{
    try(InputStream is = new FileInputStream(new File("foo.txt")))
    {
        
    }
    catch (final FileNotFoundException e)
    {
        // do special stuff for the exception case
    }
}

try-with-resources

Include more resources

  • You can add several resources
  • Closing in reversed order
// The New Kid
public void test()
{
    try(
        InputStream  is = new FileInputStream(new File("foo.txt"));
        OutputStream os = new OutputStream(new File("bar.txt"));
        )
    {
        // your I/O code here
    }
    catch (final FileNotFoundException e)
    {
        // do special stuff for the exception case
    }
    // This is the virtual finally block.
    // Java will auto-close "os" first
    // "is" last
}

Rethrow

Communicating the exception after handling

  • When you need handling of the exception but don't want to keep it to yourself
  • Upstream code needs this exepction too
  • Instead of raising a new exception you rethrow the one you already got
  • A new exception means a new "source" aka start point in the code, despite being the same class, it would not be the same exception
public void test() throws FoobarException
{
    try
    {
        // do all the stuff
        throw new FoobarException();
    }
    catch(final FoobarException e)
    {
        // do your error handling here

        // let the others know what the problem is
        throw e;
    }
}
Exception in thread "main" FoobarException
	at Test.test(Test.java:30)
	at Test.main(Test.java:43)

Chained with a Cause

An exception in an exception

  • Sometimes an exception has to be wrapped
  • The type cannot be thrown for instance or the API would change when a new checked exception turns up
  • Often used to sum up different exceptions into one
public void test()
{
    try
    {
        // do all the stuff
        throw new FoobarException();
    }
    catch(final FoobarException e)
    {
        // do your error handling here

        // let the others know what the problem is
        throw new RuntimeException(e);
    }
}
Exception in thread "main" java.lang.RuntimeException: FoobarException
	at Test.test(Test.java:37)
	at Test.main(Test.java:43)
Caused by: Test$FoobarException
	at Test.test(Test.java:30)
	... 1 more

Recommendations

Best practises

  • Don't catch errors
  • Avoid catching all (brute-force)
  • Don't suppress important exceptions aka InterruptedException
  • Avoid wrapping because the cause is often not seen
  • Dont't catch and suppress
  • Select checked/unchecked carefully
  • Use the right exception either by selecting an existing one or creating your own
  • Use exceptions for exceptions, not regular cases
  • Create exception when needed not upfront

Cost of Errors

Everything has a price

  • Don't use it to communicate the 99% case
  • , don't use exceptions, use custom objects
  • Avoid most of the runtime exception by carefully checking input
  • Runtime exceptions are programming mistakes, test instead of handling them silently
  • Overwrite fillInStackTrace method and avoid the cost of stack collection; this will suppress the location/origin of the exception
  • Only optimize exception handling in often called code where cost matters

Examples

  • You have to create a directory.
  • If the dir typically exists, check the existence first before creating it.
  • If the dir mostly does not exist, create it and rather catch the exception.
  • You have to parse CSV data.
  • If the lines have most of the time seven fields, catch the exception.
  • If the lines have often an incorrect count, check the count first.