You will have noticed by now that not always everything goes as planned when writing programs. We already introduced you to common error types in an earlier question and during the lectures and tutorials. As developer, you need to have a way of handling errors when they occur in order to print a helpful error message to the user and gracefully terminate the program or, if possible, recover from it and continue.
As useful as error messages can be to the user, they do not help your program to understand that an error just happened. If an error occurs within a function, it is usually the job of the code calling that function to handle it. In order to communicate to the calling code that an error happened from within a function, you have two main options:
- Return an error value
- Throw an Exception
The return value of a function can be dedicated to indicating success or failure of its execution. For example, by returning a boolean value.
public static boolean birthdayGreetings(String name, int age) { boolean success; if (age > 0) { System.out.println("All the best to your " + age + ". birthday " + name); success = true; } else { System.err.println("ERROR: The given age must be larger zero but is: " + age); success = false; } return success; }
The above code only sends birthday greetings for people older than zero. Clearly you would consider a negative number or maybe even zero to be invalid input here. Let's assume you want to keep asking the user for their age and name until they have provided valid input. Thanks to the returned error value, you can now do that. Simple check in a while loop if the return value of the function you called is false. If so, call the function again. The error message will communicate to the user that something went wrong and also indicate what it was.
Warning
The error message is not printed to the standard output stream here but the standard error stream instead (System.err) which allows you to quickly filter for error messages in a larger program and is considered good style.
Note
The error message is not printed to the standard output stream here but the standard error stream instead (System.err) which allows you to quickly filter for error messages in a larger program and is considered good style.
To practice that, write a class ErrorHandlingReturn which has two functions: birthdayGreetings as defined above and spam. The function spam should get two arrays as input parameters. One is of type String[] containing names and one of type int[] containing ages.
Note
You can assume for now that both arrays are not null and have the same length but realistically you would want to do some error checking here too!
Implement the method spam so that it loop over the arrays and calls birthdayGreetings for each entry. The first name should go with the first age, etc. If an invalid age was entered and a corresponding error value is returned, your spam method should try again for that name with a default value of 20.
For the input ["Peter","Sarah","Ivan"],[23,-5,35], your program should print:
All the best to your 23. birthday Peter ERROR: The given age must be larger zero but is: -5 All the best to your 20. birthday Sarah All the best to your 35. birthday Ivan
An automated test has been created for this exercise: ErrorHandlingReturnTest.java.
As crude as this example was, it could hopefully demonstrate how a returned error value can communicate to the calling code that something bad happened that needs to be handled. You can increase the types of errors you can return by using an integer or even a String as return value (An enum is probably best but we will get back to that).
Warning
The glaring problem with this approach is, that your return value is now blocked for the error value. What if you actually need to return some data? You could prevent that by making use of class references via the parameters but there is a different way to report errors which are exceptions.
Exceptions are a language feature which is independent of function return values and parameters. If an error occurs in a function, an exception object is created, filled with corresponding error information and handed to the calling code via a channel only used by exceptions. This is called throwing an exception.
public static void birthdayGreetings(String name, int age) { if (age <= 0) { throw new IllegalArgumentException("The given age must be larger zero but is: " + age); } System.out.println("All the best to your " + age + ". birthday " + name); }
Note
The code can be significantly simplified if errors are handled using exceptions.
You use the throw keyword and new operator to create an exception object of type IllegalArgumentException and throw it. This will immediately exit the function to the calling code.
The Java library provides a range of predefined exceptions but you can also create your own. This is, however, beyond the scope of this course.
Warning
For our purposes, particularly for the assignment, we will go with the convention, that you should throw an IllegalArgumentException if a given parameter is not as specified and it is not explicitely stated that you should use an error return value for that case. If a given parameter is null and this is not allowed by the specification, you should throw a NullPointerException.
Throwing exceptions is simple but how do you handle one in the calling code? You can do that by surrounding a function which might throw an exception with a try-catch block.
try { // all code within the try block is protected // by the corresponding catch birthdayGreetings("Peter", -5); } catch (IllegalArgumentException error) { // the catch block is able to catch IllegalArgumentExceptions and // handles them by printing the provided error message. System.err.println(error); }
Here, the error is handled by simply printing the error message provided with the exception but you could also execute any other code.
To practice this, write a public static function lowerAndTrim in a class ErrorHandlingException that takes a String parameter, removes all leading and trailing white spaces from the given String and turns all letters to lower case. The corresponding result should then be returned. Add appropriate error checking for the case that the given input is null and throw and exception if so. An empty String is allowed.
Examples look like this:
- " Hello World!" would return "hello world!"
- " No Pain, No Gain " would return "no pain, no gain"
Hint
Have a look at the Java API for the String class if you can find a suitable method to help you do that.
Write a second function formatText which gets a String array parameter and calls the lowerAndTrim function for each of them. If successful, print the returned String, if not increase a counter and continue with the next one. You do not need to print the error message to the console. In the very end, print the value of the counter.
For the input ["Hello, World! ",null,""," No Pain, No Gain ",null], your program should print:
hello, world! no pain, no gain 2
An automated test has been created for this exercise: ErrorHandlingExceptionTest.java.