C#

Things to love about Java: Exception Handling


In my quest to learn and become a better developer, I undertook an excursion into Java-land. Partly this was driven by the fact that Java is the language used for Android app development, and partly because it was time to branch out and explore my first non-Microsoft-driven development platform. Until sometime in 2010, I had only worked within various Microsoft languages, mostly VB and C# .NET and VBA/VB6.

As with any significant change, there was some initial frustration. However, as I became accustomed to the Java way of doing things, I discovered a few implementation gems within the language which I really liked. Chief among these was the exception handling model.

Java identifies two categories of exception: the Checked Exception, which well designed code should anticipate and handle, and Unchecked Exceptions, which arise from errors external to the system, or within the runtime execution of the program, and which are difficult to anticipate and/or handle in any practical sense.

Java REQUIRES that any method which might potentially encounter or throw a Checked Exception adhere to a Check or Specify policy. What this means is that any method which throws such an exception must specify such as part of the method signature, and that client code consuming the method must either handle the exception, or again specify that it will throw the same exception.

A Trivial Example for Comparison Part I – The C# Way:

By way of illustrating the difference between exception handling in C# and that of Java, we will create a simple library class called RentalAgreement (We are really just focusing on exception handling here, and this is a REALLY trivial example, so I don’t wanna hear about problems with the business logic, or clunkiness of the examples!) A rental agreement has a start date and an end date (and some additional information, but for the purpose of brevity, we will leave our class at that for the moment).

C# Example 1 – Basic C# Code:

    
public class RentalAgreement
{
    private DateTime _startDate;
    private DateTime _endDate;

    publicRentalAgreement(DateTime StartDate, DateTime EndDate)
    {
        _startDate = StartDate;
        _endDate = EndDate;
    }
}

The constructor for this class accepts two arguments, a start date and and end date. Since our business model dictates that the end date must occur AFTER the start date, we might want to set up some exception handling to ensure a valid range between the start and end dates. Our code can then propagate an exception to any client code if such an event occurs. We’ll modify our class slightly, adding a local function to compare two dates for precedence, and a if/else throw block in the constructor:

C# Example 2 – Improved C# Code:

    
public class RentalAgreement
{
    private DateTime _startDate;
    private DateTime _endDate;

    public RentalAgreement(DateTime StartDate, DateTime EndDate)
    {
        //Use local function NoPrecedence to compare the start and end dates:
        if (this.NoPrecedence(StartDate, EndDate))
        {
            _startDate = StartDate;
            _endDate = EndDate;
        }
        else
        {
            // If the end date occurs before the start date, let client 
           // code know about it:
            throw (new Exception("The end date cannot occur before the start date"));
        }
    }      

    private bool NoPrecedence(DateTime StartDate, DateTime EndDate)
    {
        if(EndDate < StartDate)
        {
            return true;
        }
        return false;
    }
}

The exception thrown in the constructor will propagate up the call stack to the client code, which can then implement some well-thought-out handling. Or not. It could be that our erstwhile developer might have overlooked the need to validate user input, or otherwise missed the potential exception case. In any case, the following test code mimics what might happen if a user were to enter a start date of 1/1/2011, and an end date of 12/31/2010:

C# Example 3 – Bad, BAD Client Code:

    
private void button1_Click(object sender, EventArgs e)
{
    DateTime startDate = new DateTime(2011, 1, 1);
    DateTime endDate = new DateTime(2010, 12, 31);

    RentalAgreement rentalAgreement = new RentalAgreement(startDate, endDate);
    MessageBox.Show("Start Date = " 
        + startDate.ToShortDateString() 
        + " : End Date = " + endDate.ToShortDateString());
}

However it happened, our hapless user, on entering the above incorrect date combination, would be faced with THIS ugliness:

Exception-Message-to-User-C-Sharp-Ex[2]

Of course, all of this might be averted if our developer implements some exception handling in his client code:

C# Example 4 – Much Better Client Code (kind of):

    
private void button1_Click(object sender, EventArgs e)
{
    DateTime startDate = new DateTime(2011, 1, 1);
    DateTime endDate = new DateTime(2010, 12, 31);

    try
    {
        RentalAgreement rentalAgreement = new RentalAgreement(startDate, endDate);
        MessageBox.Show("Start Date = "
            + startDate.ToShortDateString()
            + " : End Date = " + endDate.ToShortDateString());
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);            
        // Now do some stuff to reset the GUI so that the user can see exactly
        // where they screwed up . . . 
    }
}

A Trivial Example for Comparison Part II – The Java Way

The Java version of our class differs only slightly from the C# code. If we add the throw statement to our code in the else block before we add the throws declaration to the method signature, the compiler (I am using Eclipse) warns us that our method presents an unhandled exception, and will not compile. This is the Check or Specify policy informing us that we either need to handle the exception condition within the current method, or specify in the method signature that there is potential for the exception to occur, and that client code must provide the handling mechanism. Once we add the throws declaration in the method signature, everything is fine again:

Java Example #1 – with throws keyword

    
public class RentalAgreement 
{
    private Calendar startDate;
    private Calendar endDate;
    
    // Note the throws clause of the method signature:
    public RentalAgreement(Calendar StartDate, Calendar EndDate) throws Exception
    {
        if (this.NoPrecedence(StartDate, EndDate))
        {
            startDate = StartDate;
            endDate = EndDate;            
        }
        else
        {
            // Because this method throws a checked exception, we are REQUIRED
            // to either handle the exception condition or specify in the method 
            // signature that the exception might be thrown.
            throw (new Exception(""));
        }
    }
    

    private boolean NoPrecedence(Calendar StartDate, Calendar EndDate)
    {
        if(EndDate.getTimeInMillis() < StartDate.getTimeInMillis())
        {
            return true;
        }
        return false;
    }
}

Now, we have defined a library class containing a method which throws a checked exception. Next, lets create another silly piece of code which consumes the class, mimicking some faulty user input:

Java Example #2 – Consuming the Method

    
public static void main(String[] args)
{
    //Mimic some user input:
    Calendar startDate = Calendar.getInstance();
    startDate.set(Calendar.YEAR, 2011);
    startDate.set(Calendar.MONTH, Calendar.JANUARY);
    startDate.set(Calendar.DAY_OF_MONTH, 1);
    
    Calendar endDate = Calendar.getInstance();
    endDate.set(Calendar.YEAR, 2010);
    endDate.set(Calendar.MONTH, Calendar.DECEMBER);
    endDate.set(Calendar.DAY_OF_MONTH, 31);
    
    //Attempt to create an instance of the RentalAgreement class:
    RentalAgreement newRentalAgreement = new RentalAgreement(startDate, endDate);
    
}

The compiler flags our code at the point where we attempt to create an instance of the RentalAgreement class, and in fact will not compile as written. Why? Because the constructor of the RentalAgreement class posits that it might throw an exception, and we have not provided a handling or propagation mechanism for this. Our client code is REQUIRED by Java to either Check the exception (most often with a try . . .catch block) or Specify, again, that the exception may be raised by the current method, and declared as part of the method signature. Since the current code represents the application entry point, we will need to provide some graceful handling (with a try . . . catch block) of the exception before our application will even compile:

Java Example #3 – Client Code with Exception Handling:

    
public static void main(String[] args)
{
    //Mimic some user input:
    Calendar startDate = Calendar.getInstance();
    startDate.set(Calendar.YEAR, 2011);
    startDate.set(Calendar.MONTH, Calendar.JANUARY);
    startDate.set(Calendar.DAY_OF_MONTH, 1);
    
    Calendar endDate = Calendar.getInstance();
    endDate.set(Calendar.YEAR, 2010);
    endDate.set(Calendar.MONTH, Calendar.DECEMBER);
    endDate.set(Calendar.DAY_OF_MONTH, 31);
    
    //Attempt to create an instance of the RentalAgreement class:
    try
    {
        RentalAgreement newRentalAgreement = new RentalAgreement(startDate, endDate);
        SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
        System.out.print("State date = " + 
                formatter.format(startDate.getTime()) + 
                " : End Date = " + 
                formatter.format(endDate.getTime()));
    }
    catch (Exception ex)
    {
        // Inform the user about the error of their ways:
        System.out.print(ex.getMessage());
    }        
}

The code in Java Example #3 compiles and runs properly.

Note that not ALL exceptions receive this special treatment within Java. Unchecked Exceptions which derive from the java.lang.RuntimeException or java.lang.Error do NOT require adherence to the Check or Specify policy. In fact, because the requirement to build the Check or Specify mechanism into code is often viewed as a pain the ass, some Java developers tend to write code which throws RuntimeExceptions where in fact a checked exception is warranted, or derive their own Exception classes from RuntimeException in order to avoid writing a bunch of handling code and/or adding the throws clause to method signatures.

In my humble opinion, these folks are depriving themselves (and more importantly, consumers of their code) of one of the more useful benefits of the Java language architecture. Yes, it IS a pain in the ass to follow up and Check/Specify all those Checked Exceptions. But this requires us to construct better code, in which many of the exceptional cases which should either be pinned down with proper handling, or eliminated through design improvements and structural code changes. This ALSO provides an informative mechanism for developers who may use our libraries in their own applications, through which they will know straight away what type of exception to expect when calling one of our methods.

I am a strong fan of C# and .NET in general. But one area where the designers of the Java language got things right was in requiring such handling of exceptions, and the Check/Specify policy.

C#
C#: Using Reflection and Custom Attributes to Map Object Properties
ASP.Net
ASP.NET: Understanding OWIN, Katana, and the Middleware Pipeline
Biggy
Biggy: Interface Semantics and The Interface Segregation Principle
  • jatten

    jattenjatten

    Author Reply

    True on all counts, and consistent with your screen name! :-)

    Not sure if you found it yet, but I have an updated post in which I re-examine some of this. However, you make a very good point about the int declaration for DAY_OF_MONTH, yet fail to constrain the values.

    Very good point.

    Thanks for reading, and for taking the time to comment!


  • Devil's Advocate

    What makes Java's checked exceptions categorically different from any other ways in which a programming language might require the programmer to explicitly declare their intent more carefully at compile-time?

    For example, the string literal that you pass to SimpleDateFormat isn't checked until run-time, but it could well be faulty. The DAY_OF_MONTH is only declared to be an "int" in the interface, so while the value you gave here is valid (31), most of the values which could legally be compiled are not.

    Other languages provide ways to specify a range-of-integers as a compiler type, or even do arbitrary value checks at compile-time, yet Java only provides one very specific one: the list of checked exceptions that you declare for each method.

    It seems to me that Java not only picked just one check to allow (and in fact require), but this check doesn't even correspond to the most common mistake. Isn't a simple typo much more likely than an architectural problem? Worse, isn't a simple typo going to be much more difficult to locate and debug, since in most cases it will not even yield a stack trace?


  • jatten

    jattenjatten

    Author Reply

    @jaaosi –

    A. As I stated in the post, the code you refer to represents a trivial example, from a trivial project I used for the purpose of becoming familiar with Java. I do believe I even acknowledged that there is plenty of opportunity to mock the structure and/or logic I used.

    B. It was a trivial example used to demonstrate the exception concept. I am not suggesting the function itself represents a best practice. In fact there are probably many, many better ways to do what I am trying to do in the larger project

    C. From what I have learned, encapsulating such code (which is used throughout the admittedly trivial library I was building) within function, despite its triviality, makes for more condensed, more readable, and more concise code.

    D. Perhaps if you wanted to elaborate on the reason you take issue with the function anyway (ignoring, for the moment, the above two points) it would serve to enlighten both myself (I am teaching myself all of this, so this is how I learn) and others.

    I don't pretend to be an expert here, or anywhere else. Just an enthusiast trying to figure things out. I would love to hear more about why you feel the trivial NoPrecedence function was worthy of a comment.


  • jaaosi

    jaaosijaaosi

    Author Reply

    private boolean NoPrecedence(Calendar StartDate, Calendar EndDate) { if(EndDate.getTimeInMillis() < StartDate.getTimeInMillis()) { return true; } return false; } }

    serious?


  • Admin

    AdminAdmin

    Author Reply

    Hi Alan –

    Thanks for the links. I am looking further into this now, and while I sound effusive in my article (I was flush with discovery, and had not yet sufficient information!), I agree that all is not as great in this as I thought.

    I have mentioned recently that I am still attracted to the "check-or-specify" policy, although I feel it would be a nice language feature to make available as an option via a keyword or something. Possibly even by allowing the developer to decide whether to include the "throws" clause at the end of a method signature (hypothetical result being that tacking this on the the method signature causes the "check-or-specify" policy to be implemented).

    I am off now to read the material you linked to. I am getting enough great feedback that a follow-up post is inevitable, in which I will no doubt eat some crow. But the process and discussion has been informative.

    Thank you for taking the time!


  • Alan Smithee

    Long time Java dev here. I would recommend that you read these for some counter arguments against checked exceptions. I tend to dislike them :

    http://www.mindview.net/Etc/Discussions/CheckedExceptions

    http://java.dzone.com/articles/tragedy-checked-exceptions

    http://stackoverflow.com/questions/613954/the-case-against-checked-exceptions