Exception handling in Dart

What is Exception?

The Exception is to indicate if something unexpected happen while executing the program.

The Exception handling is to void the termination of a program, you must catch exception properly.

If the exception isn’t caught, its terminate the program.

The try statement is definition of exception handling code in a structured way.

main() {
  dynamic output = true;
  try {
    print(output++);
  } catch (e) {
    print(e); // Exception Class 'bool' has no instance method '+'.
  }
}

Try statement

A try statement consists of a block statement, followed by at least one of:

  1. A set of on-catch clauses, each of which specifies the type of exception object to be handled, one or two exception parameters, and a block statement.
  2. A finally clause, which consists of a block statement.

Note: The on clause can be omitted.

Execution of a try statement

var expr1;

  try {
    expr1;
  } on NullThrownError catch (e1, s1) {} on Exception catch (e1, s2) {} finally {}
  1. First expr1 is executed.
  2. If execution of expr1 throws with exception object e1 and stack trace s1, then e1 and s1 are matched against the on-catch clauses to yield a new completion.
  3. Then, even if execution of expr1 did not complete normally or matching against the on-catch clauses did not complete normally, the finally block is executed.
  4. If execution of finally does not complete normally, execution of the try statement completes in the same way.
  5. Otherwise if execution of expr1 threw, the try statement completes in the same way as the matching against the on-catch clauses.
  6. Otherwise the try statement completes in the same way as the execution of expr1.

Catch statement

The catch keyword is to get a reference to the exception object. Catching an exception gives you a chance to handle it:

dynamic output = true;
  try {
    print(output++);
  } catch (e) {
    print(e); // Exception Class 'bool' has no instance method '+'.
  }

Finally block

The finally keyword is to ensure that some code runs whether or not an exception is thrown.

The finally clause runs after any matching catch clauses. If no catch clause matches the exception, the exception is propagated after the finally clause runs.

The following code snippet shows how to clean up resources, even if an exception is thrown.

try {
    openConnection();
    processData();
  } catch (e) {
    logError(e);
  } finally {
    // Always clean up, even if an exception is thrown.
    closeConnection();
  }

Throw exception

The throw expression is to throw an exception

Evaluation of a throw expression

  1. The throw expression evaluated to an object, if its null object then a NullThrownError is thrown.
  2. Otherwise the throw statement throws exception object with a stack trace.
  3. If the throw expression evaluated to an Error object or subclass, and it is the first time that Error object is thrown, the stack trace will be returned by the objects stackTrace getter property.
  4. If the same Error object is thrown more than once, its stackTrace getter will return the stack trace from the first time it was thrown.

Throwing, or raising, an exception:

throw FormatException('Expected at least 1 section');

Throw arbitrary objects

throw 'Out of Stock!';

Throwing an exception is an expression, you can throw exceptions in => statements, as well as anywhere else that allows expressions:

void distanceTo(Point other) => throw UnimplementedError();

Rethrow statement

The rethrow statement is to re-throw an exception and its associated stack trace.

Execution of a rethrow statement:

Use rethrow keyword, to partially handle an exception while allowing it to propagate.

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // Runtime error
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // Allow callers to see the exception.
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}

On-catch clause

Use the on keyword to filter for specific exceptions by type, if matching an exception object e1 and stack trace s1 against a (potentially empty) sequence of on-catch clauses of the form.

on NullThrownError catch (e1, s1) {

  } on Exception catch (e1, s2) {
  }
  1. If there are no on-catch clauses (n = 0), matching throws the exception object e and stack trace t.
  2. Otherwise the exception is matched against the first clause.
  3. Otherwise, if the type of e is a subtype of T 1 , then the first clause matches, and then e 1 is bound to the exception object e and t 1 is bound to the stack trace t, and s 1 is executed in this scope.
  4. The matching completes in the same way as this execution.
  5. Otherwise, if the first clause did not match e, e and t are recursively matched against the remaining on-catch clauses

NoSuchMethod Error

The NoSuchMethodError is thrown when a receiving object (which might be null) does not implement a method.

ArgumentError

The ArgumentError is thrown by a method that encounters an unexpected argument.

Custom Exception

The custom exception is to define your own exceptions.

Throwing an application-specific exception is a common way to indicate that an error has occurred. You can define a custom exception by implementing the Exception interface:

class DbException implements Exception {
  final String msg;
  const DbException([this.msg]);
  
  String toString() => msg ?? 'DbException';
}

Multiple catch clauses

The multiple catch clause is to handle code that can throw more than one type of exception.

How it works:

  1. The first catch clause that matches the thrown object's type handles the exception.
  2. If the catch clause does not specify a type, that clause can handle any type of thrown object.
class DbException implements Exception {
  final String msg;
  const DbException([this.msg]);
  
  String toString() => msg ?? 'DbException';
}

class DbConnection {
  void OpenConnection() {}
  void ProcessData() {}
  void CloseConnection() {}
}

main() {
  var connection = DbConnection();
  try {
    connection.OpenConnection();
    connection.ProcessData();
  } on DbException {
    // A specific exception
    print('DbConnection fails');
  } on Exception catch (e) {
    // Anything else that is an exception
    print('Unknown exception. $e');
  } catch (e) {
    // No specified type, handles all
    print('Something really unknown: $e');
  }
}