V Exceptions
46. Introduction
Groovy takes on Java’s approach to raising, handling and reporting errors and exceptions:
- Errors are serious problems that usually cause an application to halt (“crash”). In most cases you won’t handle these - they’ll happen and the program will fall over.
- Exceptions are less serious and applications, with good exception handling set up, may be able to keep going.
At the heart of this model is the java.lang.Throwable1 class. Exceptions and Errors are are two sides to the Throwable family tree:
-
java.lang.Exceptionis the superclass (parent) of all exceptions -
java.lang.Erroris the superclass (parent) of all errors
When writing Groovy applications you’re likely to interact with exceptions in the following ways:
- Catching exceptions (using
try-catch) thrown by various classes, be they written by you, part of the Java/Groovy libraries or from a third-party. - Throwing your own exceptions using (
throw) to signal that something’s gone wrong in your code. - Being surprised and frustrated when exceptions “just happen”
Your goal should be to use defensive coding and testing that seeks to handle what you think is necessary but be prepared for issues to arise and use facilities such as logging to help you determine the root-cause.
- Check out the JavaDoc for Throwable↩
47. Common Exceptions
In the following sections we’ll throw some errors and exceptions (on purpose) so that we can check out some of the most common children of java.lang.Throwable you’re likely to see. As a bonus we’ll also discover a few approaches to avoiding them.
java.lang.NullPointerException
The good old NullPointerException (NPE) will haunt your debugging sessions for years to come. Basically it means that you’ve tried to call a method or access a property on an object that isn’t there (i.e. the variable is null). Let’s cause an NPE - it’s easy:
def tmp = null
tmp.class
To avoid these, check for null by:
- Using the Safe Navigation operator (
tmp?.class) - Checking the variable with an
ifstatement before trying to access it
java.lang.AssertionError
This is an Error, not an Exception and occurs when your assert expression evaluates to false:
assert true == false : 'This cannot be'
When your Groovy assertions fail you actually seem to receive a org.codehaus.groovy.runtime.powerassert.PowerAssertionError - a subclass of java.lang.AssertionError.
The assert statement is usually seen in tests and small scripts. It’s usually better to signal an incorrect state/situation using throws - more about them in a later chapter.
java.lang.NumberFormatException
Groovy can be used as a dynamic language so there are times where you may try to do something to a value that just can’t be done. Trying to convince something that it can be a number when it really can’t be will give you a NumberFormatException:
'Kitten' as Integer
If you really need a variable to be a specific type you could use the class check feature of the switch statement. In the code below I check if value is of a type within the Number family tree before I try to convert it to an Integer. If it isn’t, I could throw and exception or handle it in some other way:
def value = 'kitten'
switch (value) {
case Number:
value = value as Integer
break;
default:
println 'I should throw an exception'
}
groovy.lang.MissingPropertyException
This exception happens when you try to access an object’s property but it doesn’t have that property. In the example below, my Person class doesn’t have a name property:
class Person {}
def jim = new Person()
jim.name
Using try-catch around this sort of exception can let you duck type - an approach that uses an object’s properties and methods to determine if something is possible.
I reckon that the hasProperty() method available on all Groovy objects is a cleaner approach than the try-catch option. We can check the object has the property before trying to use it and without causing an exception:
class Person {}
def jim = new Person()
if (jim.hasProperty('name')) {
jim.name
}
groovy.lang.MissingMethodException
This exception appears when you call a method on an object that doesn’t support the method. The lack of a getName() method on the Person class will cause an exception for poor old jim:
class Person {}
def jim = new Person()
jim.getName()
There’s no hasMethod() method - it’s called respondsTo():
class Person {}
def jim = new Person()
if (jim.respondsTo('getName')) {
jim.getName()
}
The respondsTo() method just checks if the method exists but we may want to be certain that the methods exists and has the parameter list we’re after. To achieve this we need to call respondsTo() with a second parameter, a list of the method argument types we expect - respondsTo(String name, Object[] argTypes).
java.lang.IndexOutOfBoundsException
These appear when you attempt to get() an index from a list that isn’t there. The code below attempts to get the 5th element from a 3-element list:
def list = [0, 1, 2]
println list.get(5)
Note that if we’d written that code in a slightly different way, we’d get null returned rather than an exception raised:
def list = [0, 1, 2]
println list[5]
Checking list.size() (or the length property) before trying to access is another option:
def list = [0, 1, 2]
if (list.size() >= 5) {
println list.get(5)
}
Of course the for-in loop will iterate through the list and not try to give you an element that isn’t there.
def list = [0, 1, 2]
for (item in list) {
println item
}
java.lang.ArrayIndexOutOfBoundsException
If you just had to use an array (instead of a list) then you’ll get an ArrayIndexOutOfBoundsException if you attempt to use an array index that isn’t there:
Integer[] nums = [0, 1, 2]
nums[5]
We can use the length property to make sure we don’t try to access an element that isn’t there:
Integer[] nums = [0, 1, 2]
if (nums.length >= 5) {
nums[5]
}
The for-in loop is also handy for staying within the bounds:
Integer[] nums = [0, 1, 2]
for (item in nums) {
println item
}
48. Anatomy of an Exception
Throwable and all of its descendants provide a number of useful methods. Let’s start off with a very basic try-catch block so that we can then dissect the NullPointerException:
try {
def person = null
person.getName()
} catch (any) {
println "Exception received: ${any.class.name}"
println()
println "getMessage() returns: ${any.getMessage()}"
println()
println "getCause() returns: ${any.getCause()}"
println()
println 'getStackTrace() returned:'
for (item in any.getStackTrace()) {
println "${item.getFileName()}\t${item.getLineNumber()}\t${item.getClass\
Name()}\t${item.getMethodName()}\t${item.isNativeMethod()}"
}
}
any.class.name- Tells us the type of exception that was raised
- We get:
java.lang.NullPointerException getMessage()- Returns the message that was set by the thrower of the exception
- We get:
Cannot invoke method getName() on null object getCause()- This isn’t always set but can be handy to know. Essentially this is set if the exception is “relaying” another exception.
- We get:
null for (item in any.getStackTrace()) {..}- The
getStackTrace()method returns an array ofjava.lang.StackTraceElement. EachStackTraceElementcan then be further dissected to see what was happening when the exception occurred. - In my example I output a tab separated row for each
StackTraceElement- the methods that I call should be reasonably self-explanatory but you can always check the Java API for more information. - To just dump out the stack trace I could have just used
printStackTrace()
49. Try-Catch-Finally
The try-catch-finally statement can have three forms:
try-catchtry-catch-finallytry-finally
Groovy doesn’t support the try-with-resources syntax available in Java. Groovy’s closure infrastructure often takes care of closing resources for you (you may not even need a finally block).
Scope
Before getting into the specific syntax it’s important to point out that the try statement presents a block with its own scope. This means that variables declared within the try block can’t been seen outside the block. In the example below the assert fails not because score doesn’t equal 12 but because the score variable is out-of-scope:
try {
def score = 12
} catch (any) {
}
assert score == 12
In fact, the score variable won’t be visible to the catch block or a finally block. If you do need to access score outside of the try block then you should declare it before the try:
def score
try {
score = 12
} catch (any) {
}
assert score == 12
try-catch
The basic format of try-catch is as follows:
try-catchtry {
//statements...
} catch (<Exception type> <var>) {
//statements...
}
If an exception occurs within the try block:
- The catch variable (
var) is set to the exception instance - If
Exception typeis provided (this is optional) then thecatchblock will only be activated if the exception is of that type.
In the following example I generate an exception on purpose by dividing 10 by 0. The catch block is set up to catch any exception raised:
try {
10 / 0
} catch (any) {
println any.message
any.printStackTrace()
}
As we saw in an earlier chapter, exceptions in Groovy are objects - they have fields and methods, allowing me to display the message within the exception as well as display the stack trace.
An Exception type can be provided for a catch block. In the example below I only catch an ArithmeticException - any other type of exception is raised up to the caller:
try {
10 / 0
} catch (ArithmeticException e) {
println 'I just caught an ArithmeticException'
}
A try-catch statement can consist of multiple catch blocks, each targeting specific exceptions. In this next example I explicitly catch ArithmeticException and have a default catch to pick up any other exception raised:
try {
10 / 0
} catch (ArithmeticException e) {
println 'I just caught an ArithmeticException'
} catch (any) {
println 'What just happened?'
}
In this next example I explicitly catch IllegalStateException and ArithmeticException and have a default catch to pick up any other exception raised:
try {
10 / 0
} catch (IllegalStateException e) {
println 'I just caught an IllegalStateException'
} catch (ArithmeticException e) {
println 'I just caught an ArithmeticException'
} catch (any) {
println 'What just happened?'
}
If we wanted to handle a number of exception types (sometimes called a multicatch) in the same manner, the | operator can be used within the same catch to provide a separator for the exception types:
try {
10 / 0
} catch (IllegalStateException | ArithmeticException e) {
println 'I just caught an exception I want to handle'
} catch (any) {
println 'What just happened?'
}
The last catch block (catch (any)) is a very useful one to reflect on when considering how you deal with exceptions. By providing a catch block you are flagging an intent to actually do something useful with an exception. If you don’t actually intend to add any value to the situation then I’d suggest not catching the exception. That way it will pass up the chain to the calling code (which may chose to actually handle the exception). The buck stops at the top-level code (e.g. the script) and, without explicit handling, the exception will be displayed and the program halted.
My preference is to use try-finally if I just want to tidy up resources (such as files) if an exception occurs - that way the exception will pass up to the caller but I don’t get in the way. In most cases I use the explicit form of catch and indicate which Exception type I am prepared to handle.
try-catch-finally
The try-catch statement can have an optional finally block. This is run regardless of if an exception is raised. The finally block doesn’t receive any incoming parameter and can appear no more than once per try:
try-catch-finallytry {
//statements...
} catch (<Exception type> <var>) {
//statements...
} finally {
//statements
}
In the code below, the println in the finally block will run regardless of whether an exception was raised or the try block completed successfully:
try {
10 / 0
} catch (any) {
println any.message
} finally {
println 'I think we survived'
}
But why use finally? Exceptions are used in a large number of places, including:
- Failure to read/write a file
- Failure to access a database
- Trying to access
null
The finally block comes in handy when you need to tidy up resources before either completing the try block or losing control to an exception. You’ll most commonly see finally used to close files, disconnect from databases or return the system to a state in which it can continue.
try-finally
The third form of the try statement doesn’t provide a catch block:
try-finallytry {
//statements...
} finally {
//statements
}
Essentially we’re indicating that any exceptions will just be raised up to the caller but we need to tidy up some resources before losing control:
try {
10 / 0
} finally {
println 'Finally block has been evaluated'
}
50. How Much Should We try?
Whilst you could wrap an entire script/method/closure in a huge try statement this will quickly get confusing - imagine how many catch blocks you’ll need at the end of a 150-line script! Yes, you can catch (any) but you’d really have no clue what caused the exception.
Take the following example:
try {
methodOne()
methodTwo()
//148 more lines of code :)
} catch (MethodOneException e) {
} catch (MethodTwoException e) {
}
I tend to “localise” my try blocks so that they deal more specifically with the exception arising from a specific method call:
try {
methodOne()
} catch (MethodOneException e) {
}
try {
methodTwo()
} catch (MethodTwoException e) {
}
Although that last example results in more code I suggest it’s more useful as it helps localise the exception. In the first example I could end up catching a MethodTwoException from my call to methodOne - probably not what I really intended. If both methods throw the same exception type then localising really helps as I’ll know which method threw the exception.
Additionally, if you go with the rule that you’re only catching exceptions you’re prepared to actually do something with, a lot of your code may not be wrapped with a try statement at all!
51. Causing an Exception
The throw statement will cause an exception to be thrown. You’ll use this inside your own code to either throw an exception type already provided by Java or Groovy or to throw an exception type that you have developed. Remembering that exceptions are classes, you can create your own subclass of Exception for your own project. Let’s take a small step back for now and see how we throw an exception.
Often, new is used within a throw statement to initialise a new exception instance. The example below will throw a new instance of the IllegalStateException. As part of creating the new exception instance I pass it a message to help inform the user as to why they’re getting an exception:
def numerator = 10
def denominator = -1
try {
if (denominator < 0) {
throw new IllegalStateException('I haven\'t learnt how to divide negative numb\
ers')
} else {
return numerator / denominator
}
} catch (any) {
println "${any.message} (${any.class.name})"
}
You can use new to create a new instance of an exception but this won’t cause the exception to be thrown - you’ll need to throw it. The long-form version of the previous example would appear as:
def ise = new IllegalStateException('I haven\'t learnt how to divide negative nu\
mbers')
throw ise
In the code above I throw an exception (IllegalStateException) to indicate a limitation in my program. As before, the catch will receive the thrown exception but this time it could be either the IllegalStateException or the ArithmeticException:
def numerator = 10
def denominator = -1
try {
if (denominator < 0) {
throw new IllegalStateException('I haven\'t learnt how to divide negativ\
e numbers')
} else {
return numerator / denominator
}
} catch (IllegalStateException e) {
println 'I just caught an IllegalStateException'
} catch (ArithmeticException e) {
println 'I just caught an ArithmeticException'
} catch (any) {
println 'What just happened?'
}
The code above makes a little more sense than my earlier example as denominator may be 0 or a negative number and we are concerned with both possibilities. As mentioned earlier, we can use | to handle more than one exception type within the same catch block:
def numerator = 10
def denominator = -1
try {
if (denominator < 0) {
throw new IllegalStateException('I haven\'t learnt how to divide negativ\
e numbers')
} else {
return numerator / denominator
}
} catch (IllegalStateException | ArithmeticException e) {
println 'Stand back, I know how to handle this'
} catch (any) {
println 'What just happened?'
}
Constructing an Exception
There are a few approaches to constructing an exception, the main ones being:
new IllegalStateException()- Creates a new instance without a message
new IllegalStateException('Sorry, Dave, I cannot allow this')- Creates a new instance with a message
new IllegalStateException('Sorry, Dave, I cannot allow this', e)- Creates a new instance with a message and provides the exception (
e) that caused the exception you’re now throwing.
Let’s look at an example usage of the last variation. In the following code snippet I catch two possible exceptions and bundle e into a new exception instance but pass e to the new instance so that the caller could determine the cause:
catch (IllegalStateException | ArithmeticException e) {
throw new IllegalStateException('Unable to perform operation', e)
}
Upon having the exception thrown at me I could use the getCause() method to determine if there was an underlying cause.
Creating Your Own Exception
Whilst you can write your own Exceptions (they’re just objects after all), you should look to the pre-packaged ones and see if they meet your needs. Alternatively, consider if you can write a subclass that provides refinement to an existing exception rather than just extending Exception.
52. Catching Errors
Before we go too far on this one please note that errors indicate serious/abnormal problems and shouldn’t be caught.
Errors are different to exceptions as they indicate a fundamental issue rather than a recoverable problem. It’s highly unlikely you’ll ever need to handle an error so treat it like a cold and don’t try to catch one. They may “crash” your program but it’s likely any treatment that you try to apply will make things worse. For example, if somehow you managed to catch a java.lang.OutOfMemoryError, how would you respond?
One more time: don’t catch errors.
Errors are objects and can be caught much like exceptions but the following won’t work:
try {
assert true == false
} catch (err) {
println 'I caught the error!'
println err.message
}
The code above doesn’t do what we hoped as, by default, the catch is looking for Exception or one of its subclasses1 - probably because we shouldn’t catch errors.
In order to catch an error we must provide Error (or a subclass of Error) as the data type in the catch:
try {
assert true == false
} catch (Error err) {
println 'I caught the error!'
println err.message
}
- This is why, when we write our own exception we extend
Exceptionrather than implementThrowable.↩
53. Methods and Exceptions
The code in this chapter is rather skewed as we’d rarely throw an exception and catch it within the same try-catch block. Rather, we’re most likely to throw an exception from a method back to the caller and we use try blocks to catch exceptions from methods we’re calling.
Throwing an exception from a method results in no return value being returned to the caller - the caller needs to handle the exception. It’s important to note that, where the try and/or catch block contain a return statement, the finally block will be evaluated before the return is actually processed. In the next two examples below, the println in the finally block will always display.
return in catchdef testMethod() {
try {
10 / 0
} catch (any) {
return
} finally {
println 'Finally block has been evaluated'
}
}
return in trydef testMethod() {
try {
return 100
} catch (any) {
println 'Exception'
} finally {
println 'Finally block has been evaluated'
}
}
testMethod()
We’re about to get to the section on Methods.