Items 57-65
Last modified Fall 2012
Paul Ammann
Don’t do this!
try{ int i=0; while (true) { range[i++].climb(); }
}catch(IndexOutOfBoundsException e){}
Note that code may hide unrelated exception!
Implications for API design
A well designed API must not force its clients to use exception handling for ordinary flow control
Provide state testing method instead of forcing client to catch exception.
Do this instead for (Mountain m: range) { m.climb(); }
Note:
(implicit) “State Testing” method: i < range.length
No possibility of unintentionally hiding
IndexOutOfBoundsException from climb()
Bottom line: Don’t use exceptions for ordinary flow control
How about?
try{
Iterator i = collection.iterator(); while (true) {
Foo foo = (Foo) i.next(); ...
}
}catch(NoSuchElementException e){}
versus: for (Iterator i = collection.iterator(); i.hasNext();){
Foo foo = (Foo) i.next(); ...
}
Two basic options
State testing method
Doesn’t work in concurrent environment
State may change between state test and actual call
May be undesirable if state testing method is as much computation as actual call
Then, it’s better to have a distinguished return value
Distinguished return value
May not be an unused special value
Example: “null” for Object return types
Iterator next() method can legitimately return “null”
Faulty use of distinguished return value model harder to detect
Unchecked exceptions indicate programming errors
Precondition violations
Recovery is impossible
Checked exceptions indicate recoverable conditions
Force the caller to handle the exception
Use unchecked exceptions if
client has nothing to do
OutOfMemoryException client knows better
doesn’t need try-catch block; if-then block would suffice.
Use checked exceptions if client has some reasonable action
IOException Calling code is correct, but exception can still be raised!
try{ obj.action(args)
}catch(SomeCheckedException e){ throw new Error (“Assertion Error”);
} // should never happen
What is the point of making a client do this?
Conditions for using checked exceptions:
Exceptional condition cannot be prevented by proper use of the API
Programmer can take some useful action
Standard Transformation: if (obj.actionPermitted(args)) { obj.action(args);
} else {
// Handle exceptional condition
}
Or even simply (where appropriate): obj.action(args); // Client allows call to fail
IllegalArgumentException
- Inappropriate parameter; several special cases
NullPointerException (param null where
prohibited)
IndexOutOfBoundsException (index param out of range)
ClassCastException
IllegalStateException
Object state is inappropriate for method invocation
Object may not be initialized before calling accessing its state
Special subtype: ConcurrentModificationException
Concurrent modification detected where not allowed
UnsupportedOperationException
Object does not support the method
Substitution principal
Reasons to use standard exceptions:
Using your API is easier
Reading your programs is easier
Performance advantage (relatively minor)
No additional namespace to manage
Note that standard Java exception list is more than the 6 mentioned in Bloch
Propagated exceptions may make no sense
Higher layers should translate lower level exceptions
// Bad example : public int min (int[] a) { int minVal = a[0]; // Bad: Throws IndexOutOfBoundsException for (int val: a) {if (val < minVal) minVal = val;} return minVal;
}
Exception Chaining Transformation: try {
// Use low level abstraction to satisfy contract
} catch (LowerLevelException cause) { throw new HigherLevelException (cause);
}
Achieves 2 very different goals
Gives client exception at expected level of abstraction
Also preserves low level detail for failure analysis
Checked Exceptions:
Declare (required by compiler)
And Document (with @throws tag in JavaDoc)
Unchecked Exceptions:
Document (with @throws tag in JavaDoc)
Don’t shortcut with superclass exceptions
Example: Don’t catch “Exception” instead of
NullPointerException
Ok to document common exceptions at class level
Example: “All methods in class throw NullPointerException if a null object reference is passed in any parameter”
Uncaught exceptions result in printing of stack trace, including “detail” messages
These messages should contain values of all parameters that “contributed to the exception”
In other words, include relevant state information
Example: IndexOutOfBoundsException includes upper bound, lower bound, and offending index
Failure atomicity:
A failed method invocation should leave the object in the state that it was in prior to the invocation
Ways to achieve this effect:
Design immutable objects
Check parameters for validity before performing the operation
Order the computation – parts that fail come before modification
Write recovery code – cause the object to roll back its state
Perform the operation on a temporary copy of the object
public int addMax(List<Integer> list, Integer x) throws …
//pre: true
//post: if x is not max w.r.t. list, throw IAE
// else append x to list
Don’t throw exception in between modifications to state variables
Procedure should have an atomic effect throw exception in the beginning if you are not sure you can pull it off
public Object pop() throws … {
//Requires: this != EmptyStack
//Modifies: this
//Effects: pops this and returns top
Object result = elements[--size]; elements[size] = null; return result;
} // Note: Client can corrupt state – oops!
public Object pop() throws … {
//Requires:
//Modifies: this
//Effects: If this empty throw ISE
// else pop this and returns top if (size == 0) { throw new ISE(…);
Object result = elements[--size]; elements[size] = null; return result;
} // Note atomic execution, normal or exception
Programmers should never leave a catch block empty
Its easy to ignore exceptions, but don’t!
try{
...
}catch(Exception e){
} // empty catch block wont raise complaints
// from compiler! Don’t do this!
If its empty, its highly suspect
The purpose of exception is to force programmers handle exceptional conditions
At least have a comment which explains why its ok to ignore this exception