Exceptions and Errors are together known as Throwables. The Throwables are positioned like so in the Object hierarchy:
| Code Block |
|---|
java.lang.Object
java.lang.Throwable
java.lang.Error
java.lang.Exception
|
Errors are fatalities that we would normally want to cause a program failure, while Exceptions are events that we would normally want to handle in our program. An example of using them with a try-catch statement, a 'try' clause followed by a 'catch' clause:
| Code Block |
|---|
//assert 1 == 0 //AssertionError when uncommented
//try{ assert 1 == 0 }catch(e){}
//AssertionError when uncommented: Exceptions, not Errors, are caught here
try{
assert 1 == 0
}catch(Error e){}
//by specifying Error, prevents bad assertion from causing program failure
try{
assert 1 == 0
}catch(Throwable e){} //specifying Throwable also prevents program failure
//try{ assert 1 == 0 }catch(Object o){}
//compile error when uncommented:
//only Throwables and its subclasses may be caught
|
A common idiom for asserting for exceptions is:
| Code Block |
|---|
try{
'moo'.toLong() //this will generate an exception
assert false //asserting that this point should never be reached
}catch(e){
assert e in NumberFormatException
}
|
Some common exceptions associated with Groovy:
| Code Block |
|---|
assert new java.lang.ArithmeticException()
assert new java.lang.ArrayIndexOutOfBoundsException()
assert new java.lang.NullPointerException()
assert new java.io.IOException()
|
We can put code within a 'finally' clause following a matching 'try' clause, so that if regardless of whether the code in the 'try' clause throws an exception, the code in the finally clause will always execute:
| Code Block |
|---|
def z
try{
def i= 7, j= 0
try{
def k= i / j
assert false //never reached due to Exception in previous line
}finally{
z= 'reached here' //always executed even if Exception thrown
}
}catch(e){
assert e in ArithmeticException
assert z == 'reached here'
}
|
We can attach more than one 'catch' clause to a 'try' clause, and attach a 'finally' clause also:
| Code Block |
|---|
class E1 extends Exception{} //we can define our own exceptions
class E2 extends Exception{}
class E3 extends Exception{}
try{
def z
//multi-catch try-block with finally-clause...
try{
throw new E2()
assert false
}catch(E1 e){
assert false
}catch(E2 e){
z= 'reached here'
throw new E3() //uncaught exception because only one catch clause executed
}catch(E3 e){
assert false //never reached
}finally{
assert z == 'reached here'
throw new E1()
assert false
}
}catch(E1 e){} //catches exception thrown in embedded finally clause
|
With the multi catch block (since Groovy 2.0), we’re able to define several exceptions to be catch and treated by the same catch block.
| Code Block |
|---|
|
try {
/* ... */
}catch(IOException | NullPointerException e) {
/* one block to handle 2 exceptions */
} |
An exception will ripple up through the nested blocks, executing only code in 'finally' clauses, until caught, or the thread terminates.
| Code Block |
|---|
class MyException extends Exception{}
def z
try{
try{
throw new MyException()
assert false
}
}catch(e){
assert e in MyException
z= 'been here'
}
assert z == 'been here'
|
Exceptions will also ripple through function and method invocations
| Code Block |
|---|
class MyException extends Exception{}
def z= []
def met(){
throw new MyException()
}
try{ met(); assert false }
catch(e){assert e in MyException; z << 'function'}
class M{
def m(){ throw new MyException() }
}
try{ new M().m(); assert false }
catch(e){assert e in MyException; z << 'method' }
def c= { throw new MyException() }
try{ c(); assert false }
catch(e){assert e in MyException; z << 'closure'}
assert z == ['function', 'method', 'closure']
//Method embedded in closure...
def z2
def d= { new M().m(); assert false }
try{ d(); assert false }
catch(e){assert e in MyException; z2= 'closure d'}
assert z2 == 'closure d'
|
We can mark a function or method indicating what type of Exception it might throw. This is a useful documentation feature:
| Code Block |
|---|
class MyException extends Exception{}
def z= []
def met() throws MyException{ // 'function met() may throw MyException'
throw new MyException()
}
try{ met(); assert false }
catch(e){assert e in MyException; z << 'function'}
class M{
def m() throws MyException{ // 'method m() of class M may throw MyException'
throw new MyException()
}
}
try{ new M().m(); assert false }
catch(e){assert e in MyException; z << 'method' }
assert z == ['function', 'method']
|