Using exceptions in Java

By Confusion on Sunday 19 October 2008 13:58 - Comments (19)
Categories: Java, Software engineering, Views: 28.450

Last week it struck me that I've never really used exceptions in Java properly, despite having written thousands of lines of Java code over the last three years. Of course I have written plenty of try-catch blocks, logged, chained and wrapped exceptions in my own custom exceptions, but ultimately, I hardly ever use them like they were intended: as a replacement for result codes and a means to easily propagate 'failures' up the stack. Instead of using exceptions, I often use result objects that simply wrap the actual result object, together with a boolean indicating whether the call was succesfull and an optional failureMessage.

The question that immediately followed is: why? Why has it taken me so long to realize the way in which exceptions are meant to be used? I think the problem is twofold:
  • The name feels wrong and
  • Nonlocal behaviour is hard to grasp
The first one is simply this: when a method fails, this often isn't exceptional. For instance, someone requests the order with ID 1529, but no order with that ID exists. The method fails and I could throw an 'OrderDoesNotExistException', but that just feels wrong, because the situation simply isn't exceptional. I think this can be mitigated by simply changing the name of such a custom Exception to 'OrderDoesNotExistFailure' or the even simpler 'OrderDoesNotExist'.

The second is a more subtle problem, which I think has to do with the fact that code is easier to understand when you handle the result of a method call immediately, even if that consists only of 'inspect the boolean in the wrapped object and branch'. When throwing an exception, the flow continues somewhere far from the original method call and this is less intuitieve. However, one can develop an intuition for it, if one properly designs the use of exceptions beforehand. And that is the ultimate problem: I've never thought deeply enough about the way one should/could use exceptions.

Volgende: Matching lines in multiline regular expressions 10-'08 Matching lines in multiline regular expressions
Volgende: An example of why I like Python 10-'08 An example of why I like Python

Comments


By Tweakers user Snake, Sunday 19 October 2008 14:16

Exception throwing takes more resources than a simple if to determine weither the ID exists.

Exceptions are not a replacement for result codes, they are for exceptional problems, like out of memory, or writing to a disk that has been removed.

But checking if an ID exists can be handled very easily with an if. Don't misuse exceptions for that.

By Tweakers user Sebazzz, Sunday 19 October 2008 14:21

Exceptions are used for situation you don't expect. For instance, a Messenger program expects a working internet connection. A valid login is not expected and should be taken care of by an if clause. However, if the internet connection fails a exception should be thrown since its not expected to happen.

By Tweakers user momania, Sunday 19 October 2008 14:38

But checking if an ID exists can be handled very easily with an if. Don't misuse exceptions for that.
Knowing the internal id of an object suggests that you already have another reference to that id, so the id should exist. Then getting the object for that id should never fail, so an exception is then correct to throw.

There's a big difference in getting an object by its id, or doing a real search. Getting it by id is not a search imo and may therefore throw an exception if the object doesn't exist.

By Tweakers user Confusion, Sunday 19 October 2008 15:30

@Snake and Sebazzz:
You are missing the point of my blog posting and making the exact same mistake I made: you are both assuming things based on the name of the thing, instead of on the actual intentions as laid out by the language designers.

What is 'exceptional' and what isn't is largely a matter of opinion. If something is truly exceptional, a program could not possibly anticipate it. In my book, a netwerk hickup that causes a database connection to fail is not exceptional. It is something that is bound to happens on occasion and something that my code is prepared for. It retries or informs the user of a failure that is probably temporary. If 99 out of a 100 times, users request valid ID's, but once in a while someone mistypes and the ID doesn't exist, than that situation is, in common usage of the word, more exceptional than the above network outage. Why shouldn't I throw an exception, if the designers intended exceptions for this explicit use: to transparantly return a result to somewhere higher up the call stack?

To head Snake of: premature optimization is the root of all evil. I will not return integer result codes instead of throwing exceptions, because it might make my program run 0.01% faster. ;)

@Momania:
Well, "select data from orders where id = ?" causes the database to search for your order, so I think it's normal to call something like that a search. Even when it is a 'real' search, I don't think that matters. What matters, in the situation that I am thinking of, is that a method call is part of a series of method calls, the failure of each of which should 'cancel' the entire request. I think throwing an exception in such a case is cleaner than my current solution:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public GetOrderWrapper getOrder(int orderId) {
  // First get the 'bare' order from the database
  DbOrderWrapper orderWrapper = getOrderById(orderId);
  if (orderWrapper.isSuccesful()) {
    DbOrder order = orderWrapper.getOrder();
    // Left out a number of calls that fetch additional information about the 
    // order from other systems and repeat this pattern of wrapping-and-testing
    // numerous times.
    return new GetOrderResultWrapper(getOrderResult, true, null);
  }
  else {
    return new GetOrderResultWrapper(null, false, orderWrapper.getFailureMessage());
  }
}


Oh noes!!11!, no single point of return :+

[Comment edited on Sunday 19 October 2008 15:54]


By Tweakers user momania, Sunday 19 October 2008 16:23

Well, "select data from orders where id = ?" causes the database to search for your order, so I think it's normal to call something like that a search.
Sure, from your database's point of view, that's a search, but from your service layer within your application it's just getting an object by its id. Depends on the context. :)
I think throwing an exception in such a case is cleaner than my current solution:
That is some nasty code :P

By Tweakers user Phyxion, Sunday 19 October 2008 18:29

The examples you make here with Order isn't where are Exceptions are for, as Sebazzz points out too.
Exceptions should be used when something is an exception (Something you don't expect).

By Tweakers user H!GHGuY, Sunday 19 October 2008 19:53

Don't walk the path of exceptions without truly understanding their intention.
You're now making a pledge for what is called 'return by exception'. By writing this blog you underlined the fact that you do not master the meaning of exceptions.

First go looking for Design by Contract (http://en.wikipedia.org/wiki/Design_by_contract).
The contract is the formalization of these obligations and benefits. One could summarize design by contract by the "three questions" that the designer must repeatedly ask:
* What does it expect?
* What does it guarantee?
* What does it maintain?
Exceptions are used to specify that the function encountered an "exception" to the first question; in other words, the function expected something wich is not true.


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/** Find an order by it's ID
 *
 * @param id ID of the order
 *
 * @pre DB is connected properly
 * @pre 0 or 1 items with id
 *
 * @post DB is connected properly
 *
 * @return Requested order or null when not found
 **/
Order GetOrderByNumber(int id)
{
   if (!this.DBLayer.IsConnected())
      throw new DisconnectedException(); 
   List<Order> orders = this.DBLayer.Execute("select * from orders where id = ?", id);

   if (orders.size() > 1)
     throw new KeyConstraintViolationException("orders")

   if (orders.empty())
     return null;
   else
     return orders[0];
}



In my opinion this kind of code is how things should look.
When your precondition is violated you should throw an exception. For every other case there must be a valid return code or something similar. If you cannot signal an expected failure there most likely is a way to refactor your code to make it so and end up with a cleaner interface.

By Tweakers user momania, Sunday 19 October 2008 20:09

In my opinion this kind of code is how things should look.
By the looks of it you don't do java ;)

In java there are checked and unchecked exceptions. In case of getting some object by its id, you can throw an exception if not found and the caller has to catch it. (Design by contract ;) )

Returning null causes more problems (unchecked NullPointerExceptions anyone? ;) ).
And yes, everybody forgets now and then to check values for null. So beter off throwing checked exceptions.

By Tweakers user Confusion, Sunday 19 October 2008 22:50

@Phyxion:
You fail to address the issues I raised in my first comment, concerning the important question: when is something 'exceptional'? Your intuition doesn't cut it, concerning the intention of the designers of the language.

@H!GHGuY:
As momania points out, declaring checked exceptions is design by contract. I make explicit that the method will throw 'OrderNotFoundException' to signal that it will not return a proper result. The variable to which the result would have been assigned will usually remain null, but using 'null' as a special signal value, again as momania pointed out, causes a lot of problems.

Moreover, when returning 'null', you lose information about the cause of the failure: I cannot pass a proper message to the user. Hence the example with the wrappers: it provides the ability to always provide a failureMessage... but that is exactly part of what checked exceptions where designed for. It prevents a mess of wrapper object or special integer return values corresponding to certain failure modes. Instead, you get an object with a clear name, a clear failuremessage and an associated stack trace. You can chain them, wrap them in your own exception to provide API users with a single exception to catch, etc.

[Comment edited on Sunday 19 October 2008 22:57]


By Tweakers user latka, Sunday 19 October 2008 23:30

Expecting your users to enter only valid values would, in my book, be exceptional. The example you gave is precisely where exceptions shouldn't be used. Exceptions are there to handle things that you cannot control or indicate braindead conditions from which you cannot recover. I'm pretty sure that the Java Certified Programmer exams list the whens and hows of exceptions.

The most important thing to consider is (assuming you do this for a living) is: what would my fellow teammembers do. I've seen applications where parts are written such that in a normal control flow many exceptions are used, whereas others used return-values for everything. This is a real nightmare to maintain, so apart from what is technically possible (using exceptions to replace return-codes) or JCP mandated (use exceptions for stuff that you don't expect) align with fellow developers.

By Tweakers user latka, Sunday 19 October 2008 23:32

btw. from your example it's not clear, but having lower-layers propagate error messages to users is, in my book, a design failure: if I have a French customer he is not going to be happy when I display a 'Sorry, database couldn't be found' message because that was propagated from a lower layer. That's another reason to shun overuse of exceptions in my book.

By Tweakers user Confusion, Monday 20 October 2008 07:34

@latka:
Concerning your second comment:
The appropriate error message can usually only be determined at the point where an exception is thrown or first caught, so it necessarily has to be propagated. However, that doesn't mean it has to be a hardcoded string. It may be an i18n'ed string and it could also be an error code, that is mapped to an i18n'ed string later on. Failing to allow for proper internationalisation is an independent issue of exception usage.

Concerning your first comment:
Note what Sun has to say about it in the Java tutorials. This clearly shows that they advocate using exceptions to simplify the code.

For instance, the Java standard library declares FileNotFoundException on several methods. When invoking such a method, they do not first check whether the file exists: catching the FileNotFoundException constitutes that check. It is an exceptional condition, insofar that one may expect that, in most cases, the file will exist. It would be silly to insist on using File.exists() on files passed to such a method, when that is the exact call whose failure is used to throw a FileNotFoundException!

Of course, you may disagree with the intent and design of Java checked exceptions. However, my point here is simply that this was the intention and that I missed that.

[Comment edited on Monday 20 October 2008 07:42]


By Tweakers user momania, Monday 20 October 2008 07:50

Expecting your users to enter only valid values would, in my book, be exceptional.
Users being able to enter invalid values is exceptional and a design failure. ;)

In the example of the order and its id, the user never looks up an order by that it. He would always click on a link for order details from an order list. So the id of the order is always known, making the order not found exceptional (or the user manipulates the link, which is exceptional egain) :)

By Tweakers user Confusion, Monday 20 October 2008 09:54

@momania:
Last saturday I called a company to complain about a delivery. They asked me for the orderId as written on the receipt and then informed me that the order did not exist. I repeated the number and it seemed she had misheard/mistyped, because the second time it worked. However, in that situation, someone definitely enters an orderId.

Should I validate the data beforehand, so thoroughly that I call an 'orderExists()' method that checks the database for the existence of the id or should I consider that an exceptional (which does not necessarily equal uncommon!) situation and catch OrderDoesNotExistException and inform the user that the order does not exist? Again, considering the situation where retrieveal of the order is chained with retrieval of additional information from other datasources.

By Theodoor, Monday 20 October 2008 10:53

I come up with another point of view on the subject. Let step into the life of the method which has the single task of finding an order by it Id. Or method receives an ID and he starts his job finding it, but it not there. What should he do? He can return null, but then It is not clear what happens, it is not a proper answer for his 'problem' . He might return an empty order. Although i prevents nullpointer exceptions, it not a very good plan to do. What remains is shouting out that there is no order with the ID. Shouting out is the same as throwing an exception.
For me exceptions should be thrown when the method can not handle the result in a normal way, i.e. according to its 'contract'. You could put in the contract that the method return a null when there is no order found for an ID, but I think that is -mostly- bad programming.

By Tweakers user Turiya, Monday 20 October 2008 12:13

There's a good article on javaworld about exactly this issue, as many people have the same problems deceding when to use exceptions. http://www.javaworld.com/...998/jw-07-techniques.html A few rules of thumb when it comes to exception handling:

"Avoid using exceptions to indicate conditions that can reasonably be expected as part of the typical functioning of the method." For instance, FileInputStream.read() returns '-1' when it reaches the end of a file, because hitting the end of a file is normal and -1 is not a possible valid return value. However, DataInputStream.readInt() cannot return -1 when reaching EOF, because -1 is a valid return value. And what if EOF is reached after only 3 of the required 4 bytes for an Integer? So it throws an Exception.

"An exception indicates a broken contract." This means that when your method is supposed to return an order, but there is no order available to return, it should throw an exception. Considering the phone conversation Confusion mentions, the order not being found is clearly an exception.

By Tweakers user H!GHGuY, Monday 20 October 2008 19:17

@ Confusion

I think that you may be suffering from another violation of a rule of thumb:
each element (class, method, ...) should have a single responsibility.

When I say getOrder(), I mean getOrder(). It's not AssertOrderIsPresentAndReturnOrder().
If it's not there, then there's nothing (null) to return. The calling function should then take care of the null value.

e.g.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void onListBatchClicked()
{
  // haal int uit textbox
  OrderBatch batch = getOrderBatch(batchId);
  if (batch == null)
  {
     Status.Show("batch does not exist");
     return;
  }
  
  for (int i = 0; i < batch.size(); ++i)
  {
     Order o = getOrder(batch.getOrderId());
     if (o == null)
        throw new OrderNotfoundException();
     comboBox.Add(o.ToString());
  }
}


of

Java:
1
2
3
4
5
6
7
8
9
10
11
void onRemoveOrderClicked()
{
  // haal id uit textbox
  Order o = getOrder(orderId);
  if (o == null)
  {
    Status.Show("Order does not exist");
    return;
  }
  o.invalidate();
}



In the first case not having the order means something has become inconsistent. A batch is referring to an order which is not there. This is definately an exception.

In the second case the user merely entered an invalid orderId.

By Tweakers user Confusion, Monday 20 October 2008 21:35

@H!GHGuY:

Violating that rule of thumbis inevitable, because it is at odds with the principle of isolating seperate components of an application and giving each their own responsibility. The more complicated your application gets, the more you have to balance between such rules.

The first call into an API or application almost never has a single responsibilty: the user wants the order from the database, the data the webservice of the supplier has to add, the notes about the order from the customer-service-adminstration-system, etc. It is a single call, with great responsibility.

When you partition your software into well isolated components, the API's of those individual components also do not offer methods with a single responsibility. For instance, the method to extract information from the webservice of the supplier may well involve checking the current endpoint in a global registry and fetching the latest password from a file, as well as making the actual call and parsing the result.

As a result, there are multiple points of expected failure and as the Sun guys point out in http://java.sun.com/docs/...xceptions/advantages.html: when methods require a many steps to reach their goal, each of which breaks the process, not using checked exceptions leads to unreadable code. You get chains of if-else's and you need special error codes or wrapper objects to convey information about the nature of the failure.


Apart from the above: the meaning of a null value is often ambiguous. The trivial case is: if I am fetching something from the database of which there may be zero or more instances, what does 'null' mean and what does an empty list mean? Should such a method never return null? Isn't that inconsistent? But even in the seemingly clearcut case when the order is not present: why not return an Order object with all of its fields null? If we truly know the order isn't present, then an Order without properties actually represents this situation better, because it at least verifies that we are dealing with an absent Order. That is the one property that it does have. Null can be cast to anything. Null is like asking "Do you agree?" and getting the answer '42'. If you use null as a signal value, its semantics should be very well documented, especially because the meaning of null may change as the code evolves and because what seems intuitive to you may not be intuitive to the one that has to maintain the code later on.

[Comment edited on Monday 20 October 2008 21:57]


By Tweakers user H!GHGuY, Friday 24 October 2008 18:22

The first call into an API or application always can also have a single responsibility:
It can handle button click or similar event. That is still a single responsibility.

The exercise the developer has is to break down the responsibilities in simple and manageable sub-responsibilities and let those be handled separately.

There is an easy way of helping you reach that goal (explained for functions):
- Each function has a simple and clear name describing exactly what it does.
good: bool isValid(), Order getOrder(id), void receivePacket(packet), Item requestItem()
bad: void validate() throws(...), FetchOrderAndValidate(), SetNewPacket(), Item GetOrCreateItem()
- Each function implements just what its name says
- Each function is split into sub-responsibilities and is implemented in terms of those
- When a function has a non-splittable responsibility, it is implemented directly.
For instance, the method to extract information from the webservice of the supplier may well involve checking the current endpoint in a global registry and fetching the latest password from a file, as well as making the actual call and parsing the result.
What's wrong with:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class SupplierWebservice
{
public:
  void Connect() { if (IsConnected()) throw X; ... }
  void Disconnect() { if (!IsConnected()) throw Y; ... }
  void IsConnected() { ... }
  
  Information getInformation() { UpdateConnection(); return getInformationImpl(); }
private:
  void Reconnect() { Disconnected(); Connect() }
  void UpdateConnection() { if (RequiresPasswordUpdate()) Reconnect(); }
  void RequiresPasswordUpdate() { ... }

  Information getInformationImpl() { Result res = SendCall("getInformation"); ... ... return res.getData() }
  Result SendCall(string name) { Packet p = CreatePacket(name); p = connection.Exchange(p); return ParsePacket(p); }
}


I left out the endpoint stuff since I don't have in-depth knowledge of webservices and their implementation in Java.

Bottom-line is, every call in the above pseudocodesample has a single responsibilty, either divisable or indivisible.

You are right that another way of signalling that no order is present is to define some form of "Null Order", probably with an IsValid()/IsNull()/IsEmpty()/... call. That is generally how NaN works for floating point numbers, which is probably the best-known example.

Documentation is as important (if not more important) thant the actual code. If it says null is returned when no order is present, it's like that and that's the way it is. If code evolves 2 things must be assured:
- Documentation changes along
- all uses of the changed code must be checked and converted.
If either condition is not fulfilled expect your boss to visit and shout at you.

The thing is that good code:
- almost never needs changing.
- has minimal to no impact when changed.
What I mean with this is that when your customer/user requests feature X a good design with good code will (assuming the new feature is not a 180° turn of what is present) be easily adapted to fit this extra thing.

If someone in 2 years thinks I wrote bad code because I return null, then let him define a null-Order to return. In good code it takes 5 minutes to change that. Try changing all try-catch statements in the whole call-tree in 5 minutes.

Comments are closed