Exception-Safe Java
Warning
Topic is very controversial
Why Exceptions?
We need a mechanism
to separate the code
that generates an error with the code
that handles it
It's usually the case that where an error happened is in a different layer of where the error can be handled
A Tale of Two Exceptions
Unchecked Exceptions
- If you don't handle them, it's not a compile error
- There aren't part of the method's signature
- They will crash your program with a noisy stack trace
- This is what all languages have
Checked Exceptions
- It's a compile error if you do nothing about them
- You need to handle them or propagate them
- Part of a method's signature
- Java is the only language with them
Unchecked Exceptions
can be completely ignored
C++'s STL had a bug
for 6 years for a code
that threw an exception
but it wasn't handled
How should you use
them, according to
the language creators?
Errors
- Programmer's Errors
- Application's Errors
Programmer's Errors
- The code needs to be fixed,
e.g. remember to null check a reference - You don't need to "handle" them,
you want them to stop the application loudly
so you don't forget that your code needs to be fixed - It's important than a developer sees
the stack trace for ease of fixing the code - Use unchecked exceptions
Application's Errors
- Move the "error" to a method where an
action can be taken, probably in a different layer - Caught and do some kind of retrying / Move to presentation layer and make a message with it
- We never want to show an stack trace to an user
- Use checked exceptions
Criticisms of Checked Exceptions
#1 They can also be ignored
try {
// code
} catch (AcmeException e) {
}
There's no language
feature that
can't be misused
#2 They break encapsulation
It's true if you don't
follow best practices
Best Practices
Don't caught too soon!
#1 source of bugs!
When should you caught?
- If you are going to retry, probably
as soon as the exception is thrown - If you are going to transform it into a
message, caught on the presentation layer - If you are going to log, where you have
all the information that you need,
don't log at different classes
Avoid using them
for control flow
Use your IDE judiciously
If your IDE is giving
you a compiler error,
avoid the option of "surround with try/catch"
try {
// code
try {
// code
} catch (...) {
// code
}
} catch (...) {
// code
}
try {
// code
} catch (...) {
try {
// code
} catch (...) {
// code
}
}
// code
try {
// code
} catch (...) {
// code
}
// code
try {
// code
} catch (...) {
// code
}
Understanding the information flow is hard,
so it's easy for bugs to hide
More subtle...
String foo() {
String s = "";
try {
// code
s = ...;
// code
} catch(ABCException e) {
// handling of Error(?)
}
return s;
}
Are you sure you
won't return null?
Avoid control-flow
- try should be the first expression of a method
- there should never statements after
the last catch or finally (if there's one) - Use more than one return statement
public String getFoo(...) {
try {
// code
return ...;
} catch (ABCException e) {
// error-handling
return "";
}
}
If you need some parameters to log?
public get(Foo foo, Bar bar) {
Baz baz = foo.get(bar);
return get(baz);
}
private get(Baz baz) {
try {
// code
return ...;
} catch (ABCException e) {
LOGGER.warn(baz);
return ...;
}
}
Avoid long methods
more so, if you
need error-handling
Single Abstraction Level
- Split into smaller methods
to better handle the errors - Separate the initialization of
data with the code that uses it
Don't caught Exception
Remember that unchecked exceptions inherit from it
try {
// code
} catch (Exception e) {
// DON'T
}
Caught only what you know it would be thrown
try {
// code
foo.throwsABCException();
// code
} catch (ABCException e) {
// code
}
try {
// code
foo.throwsBazException();
// code
} catch (ABCException e) {
// Compiler Error!!
}
Don't thrown Exception
Someone will need to caught it
try {
// code
} catch(ABCException | FooException e) {
// code
}
Create your own Exceptions
public class InsufficientFundsException
extends Exception {
private double amount;
public InsufficientFundsException(double amount) {
this.amount;
}
public double getAmount() {
return this.amount;
}
}
try {
// code
} catch (ServiceException e) {
throw new InsufficientFundsException(amount);
}
Assertions
Don't handle!
try {
// code
} catch(RepositoryException e) {
throw new AssertionError(e);
}
Exception-Safe Java
By Carlos Obregón
Exception-Safe Java
- 1,082