The exception handling is an important chapter for basic programming.
It renders our programs
sturdier and more resistant
towards abnormal or error situations.
Exception handling is not per se a concept
of object-oriented programming. However, its implementation resorts
to the use of these concepts.
The goal of this episode is to present the fundamental aspects
of exception handling in Java.
In this chapter, we will discuss how to handle errors
or abnormal situations in a program.
We will see that exception handling let us anticipate
the errors and abnormal situations.
Thus, we will be able to react appropriately.
Let us begin by illustrating the usefulness of exception handling
through a simple example.
We wish to code a function "f" which calculates the inverse of a given number.
We wish for this function to handle the case where the entered value is 0
as an abnormal situation, an error situation.
We also wonder how to handle
the error itself.
Here is our first attempt :
We can have it so that, in case the entered value is 0,
we return a value chosen in advance.
To mimic the fact that this result would be close to infinity, we could decide
-- in an arbitrary way -- to return
the biggest "double" possible.
In Java, there are other predefined values to mimic the infinity (such as "infinity").
However, this is not our point here.
Unfortunately, this way to proceed is not satisdactory at all.
Firstly, this does not let the user know
that his input has resulted in an abnormal situation for the function "f"
since "x" is 0.
Here, the user is never warned of this fact.
Also, the returned result is necessarily inexact.
The use of this "f" resorts to an arbitrary convention
which is not necessarily known by other programmers.
Here, the choice of the return value is such a convention.
To overcome this lack of information that the user of the function "f" is facing
when we are divide by 0,
we could imagine to have our function "f" print an error message
whenever the entered value is 0. We would have our function print
that there is an error situation : a division by 0.
However, this does not solve all the problems
of the previous version. Indeed, we still have no idea
what we should return in an error situation.
This solution can also face criticism since it triggers so-called
"edge effects".
This means that the function "f", which is only supposed to return
the inverse of a number is now generating printings
in the terminal; this is not its usual task!
Indeed, let us imagine we are using "f" within a graphical program.
In this case, we do not want for an abnormal situation
to be reported by a message on the terminal
but rather by the opening of an alert window, for example.
Finally, even though the function "f"
is the juncture of the program where the error is detected,
it might not be the place to handle
this situation properly.
Indeed, a division by 0
could be harmful enough
to require the program to be stopped.
Also, it is possible for a division by 0 to be handled
according to the context of utilization.
This means that it is the juncture of the program calling the function "f",
informed of the context, that could give the appropriate measures
in case there is a division by 0.
A last option would be to rewrite the function "f"
so that it rather returns an error code
indicating if the division has been computed correctly.
Then, it will return the result of the division only in a normal situation,
when there is no error with the parameter.
This way to proceed is better than the previous ones.
Indeed, this way, the function calling "f"
is able to decide what to do in case of error.
The function "f" itself should not be the one taking this decision;
it should simply inform its caller upon the execution.
Then, the caller will decide what to do in case of error.
Unfortunately, this way to proceed renders the use of the function
much less intuitive.
Instead of writing something simple
which would mean
let us store into "y" the result of the inversion of "x"
computed by the method "f", we should resort to
more complex instructions.
First, we should know that "f" returns an error code
and that the normal utilization result is stored in the second parameter.
This is clearly less intuitive
than this way of writing things.
Moreover, when a method calls another which then calls another
and so on, each call could potentially result in an error code.
In this case, we must then handle the combinations
of all these error codes. Obviously, this quickly becomes unpleasant.
Now, we wish to get our hands on a mechanism
which would let us report an error situation
without triggering any of these undesired edge effects.
We also wish to possess a mechanism letting us handle the error
at another place than the one where it is detected.
This should all be done while keeping utilization flexibility,
an intuitive intuition of the different functionalities
we wish to have in our programs.
Execption handling meets all our needs.
At the place where the error is detected, we will be able to trigger an "exception".
This "exception" can then be handled at another place in the program.
Also, we are not compelled to explicitly integrate any error code
in the different functionalities we wish to implement.
The mechanism of exception handling works according
to the three points developped here.
Whenever an error or abnormal situation is detected
somewhere in the program,
we will warn the rest of the program by "throwing an object"
containing all the useful informations so that we can potentially handle
this error or abormal situation
elsewhere in the program.
"Throwing" or "triggering an exception" concretely means creating an object
which becomes available for the rest of the program.
This object can then be "caught", which means "used",
elsewhere in program so that we can handle this abnormal situation,
completely or partialy.
Finally, if the "thrown object" is never "caught",
it will trigger the stop of the program.
An unhandled thrown exception always triggers the stopping of the program.
Let us illustrate this with a concrete example.
Let us imagine, for example, that we have a main program
working with temperatures.
These temperatures are aquired through, for example, a meter.
These temperatures are then stored in an array. Our program's task
is to draw a graphic of the inverse of all the aquired temperatures.
Let us imagine these temperatures are aquired
through a not fully reliable meter.
Therefore, every once in a while, it could fail to measure
the temperature correctly
and would, for instance, add null values
into our temperature array.
The main program realizes its processing by calling
another function called "graphiqueInverse" (TN: means "invertPlot").
This function will iterate on all the temperatures of a provided array
and display the graph of their inverse.