Learning Dart(Second Edition)
上QQ阅读APP看书,第一时间看更新

Recognizing and catching errors and exceptions

As a good programmer, you test your app in all possible conditions. Dart defines a number of errors for those things that you should remedy in your code, such as CastError when a cast fails, or NoSuchMethodError when the class of the object on which the method is called does not have this method, and neither does any of its parent classes. All these are subclasses of the Error class, and you should code so that they do not occur. However, when something unexpected occurs while running the app and the code cannot cope with it, Unhandled Exception occurs. Especially, the input values that are read in from the keyboard, a file, or a network connection are dangerous. Suppose the input is such a value that is supposed to be an integer (refer to exceptions.dart); we try to convert it into an int type in line (1):

var input = "47B9"; // value read from input,
//  should be an integer
int inp = int.parse(input);                 (1)

While running the program on the console with the dart exceptions.dart command, our program will terminate with an exception:

Unhandled exception:
FormatException: 47B9
#0      int.parse (dart:core-patch:1586:41)
#1      main (file:///E:/dart/code/chapter_2/exceptions/bin/exceptions.dart:4:22)

While running in Dart Editor, the default behavior is that the debugger kicks in so you can examine the exception and the values of all the variables (you can change this behavior by navigating to Tools | Preferences | Run and Debug, and change Break on Exceptions to None). The generated FormatException is clear: the input was in the wrong format. A lot of the other exceptions exist such as IntegerDivisionByZeroException, IOException (failure to read or write a file), and HttpException (while requesting a page from a web server); they are all subclasses from the Exception class. When they are generated, they are objects that contain information about the exception. How can we handle this exception so that our program does not crash? For this, Dart follows the familiar try...on/catch...finally pattern:

  • try: To try the dangerous statement(s)
  • on/catch: To catch the exception (a specific one that you know can occur or a general exception) and stop it from propagating
  • finally: It contains code (perhaps to clean up, or close files or connections) that will be executed whether or not an exception occurs, but it is optional.

A minimal exception handler could be as shown in the following code:

try {
  int inp = int.parse(input);
} on FormatException {
  print ('ERROR: You must input an integer!');
}

This prints out the text in the on part. Use catch if you want to examine the exception object. The last clause in the try statement should be on Exception catch(e) or, even better, a simple catch(e) to stop any type of error or exception. So, the most general exception handler is:

try {
  int inp = int.parse(input);
} on FormatException { // or any other specific exception
  print ('ERROR: You must input an integer!');
} on Exception catch(e) { // Any other exception
  print('Unknown exception: $e');
} catch(e) {  // No specified type, handles all
  print('Something really unknown: $e');
} finally {
  print('OK, I have cleaned up the mess');
}

If you comment out the on FormatException part, you'll see that $e contains FormatException: 47B9.

Should an abnormal condition occur, you can generate or throw an exception in your code with throw. An example is given in the following code:

var radius = 8;
var area = PI * pow(radius, 2);
if (area > 200) {          // area is 201.06192982974676
  throw 'This area is too big for me.';
}

You can also throw a real Exception object with throw new Exception("…"). The throw keyword produces an expression, so it can be used after a => operator, like this:

clearBalance() => throw const UnimplementedError();

It is handy to remind yourself while testing that this method hasn't yet been implemented! The bottom line is to test your code exhaustively and provide exception handling for unforeseeable events that your app cannot process in a normal way.

A debugging exercise

The following little program (debuggingex.dart) results in RangeError. Use the debugger from the beginning to see where it goes wrong and correct it. In Dart Editor, double-click on a narrow column to the left of for (var i=0; i<=lst.length; i++) { in order to create a breakpoint (a blue circle). Run the program and use step over to get a new value of the i variable. Correct the program to avoid the range error. However, don't use try…catch to handle the error, because it is the programmer's mistake:

// calculate and print the squares of the list items:
var lst = [1, 2, 3, 4, 5];

void main() {
  for (var i=0; i<=lst.length; i++) {
    print(lst[i] * lst[i]);
  }
}

The following screenshot shows RangeError in the debugger:

A debugging exercise