Things to love about Java: Exception Handling

Posted on November 5 2011 08:26 AM by John Atten in C#, Java, Quality Code   ||   Comments (6)

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 Rental Agreement 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.

 

Posted on November 5 2011 08:26 AM by John Atten     

Comments (6)

About the author

My name is John Atten, and my "handle" on many of my online accounts is xivSolutions. I am Fascinated by all things technology and software development. I work mostly with C#, JavaScript/Node, and databases of many flavors. Actively learning always. I dig web development. I am always looking for new information, and value your feedback (especially where I got something wrong!). You can email me at:

jatten at typecastexception dot com

Web Hosting by