|
| Exceptions |
Introduction
Exceptions play a important role in the software industry today. One might want to handle those problems, that he didn't thought of. Thereby an exception does not always have to be an error. It is simply an 'unexpected' situation which could lead to problems in the further process of the application.
In order to blow up the existing code not unnecessarily, you try to separate the functionality, which is responsible for signaling an exception, from the 'normal' code. In order to work with Exceptions meaningful you should learn two things: Handling and Signaling Exceptions.
Attention
The Framework represented here is part of the ANSI Smalltalk and thus dialect-neutrally. Only the Exception classes are not compatible. The following code was tested with Squeak 3.4.
Important Messages
- BlockContext
- Exception
- signal
- return:
- retryUsing:
- ,
- ExceptionSet
Principle
- Handling Exceptions
The fundamental procedure is to embed the code, that could possibly throw an exception, into a block. This block provides the needed Messages.
myCodeEmbeddedInABlock
on: aException
do: aHandlerBlock
- Signaling an Exception
If a problem is determined, you instantiate a new object of an exception class and send it a signal.
aErrorCondition ifTrue: [ aException new signal ]
Example
- Handling Exceptions
Let's say we do a division by zero. The code will always throw an exception, but you should have tried it once.
[ 42.4 / 0 ]
on: ZeroDivide
do: [ Transcript show: 'Don't fool me!'; cr. ]
That doesn't help much. In order to find out something about the Exception we use another HandlerBlock that returns the dividend.
[ 42.4 / 0 ]
on: ZeroDivide
do: [ :ex | ex return: (ex dividend) ]
Or we ask the user whether he/she has a better idea.
[ 42.4 / 0 ]
on: ZeroDivide
do: [ :ex || neu |
neu := FillInTheBlank request: 'Please give me a new number!'.
ex retryUsing: [ 42.4 / neu ] ]
Now we have another problem. What if the user types in a text instead of a number? The simplest way to solve this is to handle the class Exception instead of ZeroDivide. Thus all possibilities are handled.
[ 42.4 / 0 ]
on: Exception
do: [ :ex || neu |
neu := FillInTheBlank request: 'Please give me a new number!'.
ex retryUsing: [ 42.4 / neu ] ]
Or we enumerate the possible Exceptions. In the example Halt doesn't fit purely, but it is sufficient for the demonstration. The comma is thereby a binary Message to the first Exception class with the second as argument. The result is an instance of the class ExceptionSet, which contains both.
[ 42.4 / 0 ]
on: ZeroDivide, Halt
do: [ :ex || neu |
neu := FillInTheBlank request: 'Please give me a new number!'.
ex retryUsing: [ 42.4 / neu ] ]
- Signaling Exceptions
Let's take a look at the method, which settles the division on Floats.
Float>>/ aNumber
"Primitive. Answer the result of dividing receiver by aNumber.
Fail if the argument is not a Float. Essential. See Object documentation
whatIsAPrimitive."
<primitive: 50>
aNumber = 0 ifTrue: [ ^(ZeroDivide dividend: self) signal ].
^ aNumber adaptToFloat: self andSend: #/
We do not want to look at what the method does in the best case, but what is done, if we really divide by zero.
We will give ZeroDivide additional information. The most important is however the Message signal. Thus the protected block, which 'listens' on the class ZeroDivide, will know that an Exception was thrown.
|
|
|