• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Liutauras Vilda
  • Jeanne Boyarsky
  • Devaka Cooray
  • Paul Clapham
Sheriffs:
  • Tim Cooke
  • Knute Snortum
  • Bear Bibeault
Saloon Keepers:
  • Ron McLeod
  • Tim Moores
  • Stephan van Hulst
  • Piet Souris
  • Ganesh Patekar
Bartenders:
  • Frits Walraven
  • Carey Brown
  • Tim Holloway

Some suggestions for dealing with incorrect values or exceptions

 
Marshal
Posts: 64684
225
  • Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Let's imagine you have a method like heat(int) in my Kettle class, and you find out it is giving ridiculous values.So you look long and hard at that method, seeing nothing wrong, and taking ages to realise somebody has called myKettle.heat(−1_000_000); Obviously the negative argument is the cause of the problem. So you need to find some way of dealing with negative arguments. All the following solutions are within the letter of the law about specifying programs. All the following solutions will produce complaints by the barrow‑load from all your users that you didn't test your code properly and have distributed poor quality code. Those complaints are all justified.You can now say the users haven't followed the instructions and it's their fault. That is about all there is to say about that solution. But it shows the importance of the documentation comments.Again you can say, if they had followed the instructions, they would have known. At least your kettle remains in a consistent state, not at a non‑existent temperature. But the user doesn't know anything about their mistake.Again you can say, if they had followed the instructions, they would have known. At least your kettle remains in a consistent state, not at a non‑existent temperature. But how confusing if you use the heat() method and your temperature goes from 80° to 0°
The two following suggestions are very similar:-At least the user knows something has gone wrong, even though they probably can't do anything about it at present. This is the first solution I think is even acceptable; I think the following is the only good solution to our problem:-You can be absolutely sure your method won't cool the kettle down. You can also be sure the user finds out about it. Don't use a throws XYZException for an unchecked exception. Do make sure to describe all exceptions you know about in the documentation comments. If the user lets that into production with a negative argument, you can tell them off for not reading the documentation and for not testing the code correctly
 
Campbell Ritchie
Marshal
Posts: 64684
225
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
So, what are you going to do if you suffer an exception?
Start by working out what caused it, and correct that error. The following is a good way of getting a null pointer exception:-The field retains its default value of null unless some other method assigns to it; any attempt to use that String may cause a null pointer exception. If you had written a correct constructor, that problem wouldn't have occurred. Note the use of an Objects class method to stop anybody sneaking a null in while you aren't watching:-

Prevention is better than cure

But there are places where you might suffer an exception regardless. What can you do?
  • 1: Ignore it. That is often best for unchecked exceptions. Let them crash the thread you are running, and go back to the code later to correct the problem. In a single‑threaded application, the JVM will terminate altogether.
  • 2: Similar to No 1. Let it propagate, what Knute called “bubble up” here. That is all right for checked exceptions during development. If you have production code, you want it to keep running like the Pony Express, but while you're still developing it, who cares if the program crashes?
  • 3: Do nothing. When you are a senior developer, you will know that you will never get that InterruptedException, and an empty catch is all right. But don't write empty catches at this stage.
  • 4: Log the exception in your logging file. But you need to inspect the file to find out what is going wrong. Make sure your logging has enough information to allow you to diagnose the problem.
  • 5: Rather similar to No 4: Display a message for the user to see. The more informative the message, the better the problem can be corrected. Remember who is going to read the message; printStackTrace() might be really useful for you whilst developing the app, but its output is completely incomprehensible to the average user.
  • 6: Decide you can't sensibly handle that exception here. For example, throwing an exception and catching it in the same method is simply an inefficient form of if‑else. Declare the exception in the documentation comment and (if checked) with a throws XYZException clause. You have decided it is better for the calling method to handle that exception.
  • 7: Throw the same exception again. That means you are doing some handling here, and passing it back up the call stack for further handling.
  • 8: Throw a different exception. That is probably best done with exception chaining.
  • 9: Wrap the try‑catch in a loop, as I did here. That gives the user the option of correcting a mistake, e.g. misspelt file name, network problems, and try again.
  • You can of course take several of those actions. Beware. I think it's cheating to use exception chaining to disguise a checked exception as unchecked. Beware of logging and rethrowing the same exception because you might log the same thing twice. Or you might display the same error message twice.
     
    Bartender
    Posts: 2294
    95
    Google Web Toolkit Eclipse IDE Java
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    A small variation of the bubble up example:


    It's a bit more verbose, but each method catches it, reports it (alongside some more info) and then bubbles it up.
     
    Ranch Hand
    Posts: 48
    1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    that means talking about exception propagation, if yes then checked exception are propagated with throws clause and unchecked exceptions are propagated with throw,
     
    Saloon Keeper
    Posts: 10308
    217
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    No. The throws keyword is just used to declare an exception that can be thrown from a method. It doesn't propagate anything. Unchecked exceptions may be declared, checked exceptions must be declared.

    All exceptions are thrown either by using the throw keyword, or by not catching an exception thrown by a nested method call.

    salvin francis wrote:


    Except don't catch and rethrow an exception that you've created in the same method. Just do this:

    Typically you also wouldn't log an exception and rethrow it. You simply just let an exception propagate up until you have a place where you want to handle it. Handling it can mean, among other things, logging the exception or throwing a wrapper exception, but afterwards you don't rethrow the original exception.

    Rethrowing is usually done when the original exception is appropriate, but you want to perform some cleanup to bring the object in a consistent state before letting the exception bubble up.
     
    Campbell Ritchie
    Marshal
    Posts: 64684
    225
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    Stephan van Hulst wrote:. . . Unchecked exceptions may be declared, checked exceptions must be declared. . . .

    I would think it is better style to declare only checked exceptions in the throws clause, but to describe all exceptions known about in the documentation comments. Then the user knows what to expect.

           * * * * *

    Of course, you can have unexpected exceptions. Imagine you have a kettle which is part full, and you fill it a bit more with cold water, TAP_TEMPERATURE being 10(°C).Now, we have a nice robust method which cannot go wrong as long as the users follow the instructions in the documentation comments If you have amount too large, that is equivalent to holding the kettle under the tap until it overflows and lots of water runs into the sink, so that matches a real‑life situation.
     
    Campbell Ritchie
    Marshal
    Posts: 64684
    225
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    A few minutes ago, I wrote:. . . Now, we have a nice robust method which cannot go wrong as long as the users follow the instructions in the documentation comments . . .

    And I was lying shamelessly. I shall leave it as an exercise to the reader to predict what exception might be thrown, and to suggest a simple solution that will reliably prevent that from happening.
     
    Campbell Ritchie
    Marshal
    Posts: 64684
    225
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    How many people found the spelling mistake in the third code block in this post? I didn't notice it until some time afterwards.
    How many people worked out how I was lying about the fill(int) method? I did know about the pitfall, which is why I confessed to lying so quickly.
     
    Sheriff
    Posts: 6033
    157
    Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    I Spy with my Little Eye: division by zero.
     
    Campbell Ritchie
    Marshal
    Posts: 64684
    225
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Yes, you take an empty kettle to the tap, ask, “tea, coffee, or fruit juice,” and on the answer, “fruit juice, please,” you fill the kettle with 0 of water. Boom: division by 0. Possible solution: write the method like this:-I shall leave the readers to decide whether the exception is still necessary. I don't think I should throw an exception if amount is 0 because I think 0 is a permissible value for amount.
     
    Ranch Foreman
    Posts: 3268
    20
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    salvin francis wrote:A small variation of the bubble up example:


    It's a bit more verbose, but each method catches it, reports it (alongside some more info) and then bubbles it up.



    I quite like the idea of logging additional info in case of exception (specific to the method in question), while otherwise letting the exception propagate up.  It can be very helpful to understand what happened - I often find myself trying to discover what was the order number, or some such useful id, being processed when something unexpected happened.  There's a lot of information that would just clutter up the logs if we log it every time a method is executed, but it's very desirable when an exception occurs.

    However, I would modify the technique above: please don't log the whole exception and rethrow it.  That leads to many repetitions of the same stack trace in the log file (once for each method that does the log-and-rethrow), and it has the effect of making it harder to find the extra bit of foo-specific info you wanted to record.

    I suggest something more like this:

    Note that while I am including e in the log, it's included only using string concatenation using + e.  That way I get a quick toString() summary of the exception, not the full stack trace.  It's the responsibility of whatever method catches the exception without rethrowing, to log the full exception.

    Admittedly, there's a chance that this other method won't fulfill its responsibility.  That other method might be written (or modified) by someone who doesn't practice proper exception handling.  (Hopefully, not me because I was in a rush.) . So that's why I want to include some info about the exception, that given by toString(), but not the full stack trace.  If I later find "Caught XYZ..." in the logs, without a subsequent full stack trace, I have enough info to analyze further and figure out where in the code the exception should have been logged.  Then identify the perpetrator and deal with them.  Or, if it turns out to be me, just fix it and quietly bury the evidence. ;)

    Ideally the result of this policy is a single relatively clean stack trace, preceded by the method-specific messages:
     
    Sheriff
    Posts: 13517
    223
    Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Lots of great cow-worthy discussion here. My 2 yen (since I'm posting from Japan) is that if you catch-log-and-rethrow, it's often useful to wrap the exception thrown by a lower layer in an exception more relevant to the current layer and add useful debugging information from this layer. For example, if a JDBC call throws a SQLException, you might catch and wrap that in a DAOException, adding debug info from the DAO layer. The service layer can wrap that DAOException in a ServiceException with additional information added from the service, and so on until you bubble it up as high as you need to.
     
    Ranch Hand
    Posts: 85
    2
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    Campbell Ritchie wrote:Yes, you take an empty kettle to the tap, ask, “tea, coffee, or fruit juice,” and on the answer, “fruit juice, please,” you fill the kettle with 0 of water. Boom: division by 0.



    First of all, you wouldn't use integers for temperature or volume if that was an actual physics simulation I think ;)
     
    It is sorta covered in the JavaRanch Style Guide.
    • Post Reply Bookmark Topic Watch Topic
    • New Topic
    Boost this thread!