A Tour of the Dart Language

Welcome to the Dart language tour! We'll show you how to use each major Dart feature, from variables to operators to classes and libraries, with the assumption that you already know how to program in another language.

Consult the Dart Language Specification whenever you want more details about a language feature.

Contents

  1. A basic Dart program
  2. Variables
  3. Built-in types
  4. Functions
  5. Operators
  6. Control flow
  7. Exceptions
  8. And more to come...

A basic Dart program

Here's some code that runs as-is in Dartboard and uses some of Dart's most basic features.

main() {
  // Variables and values
  var number   = 42;
  var text     = "The value of number is";

  // Console output
  print("$text $number.");
}

Here's what this program uses that applies to all (or almost all) Dart apps:

main()
The special, required, top-level function where app execution starts.
//
How you indicate that the rest of the line is a comment. Alternatively:
/* comment that might span many lines */
var
The simplest way to declare a variable.
"..." (or '...')
A string.
print()
A handy way to display output.

Variables

Here's an example of creating a variable and assigning a value to it.

var name = 'Bob';

Variables are references. The variable named name contains a reference to a String object with a value of "Bob".

Default value

Uninitialized variables have an initial value of null. This includes numbers, which are also objects.

num lineCount;
lineCount == null; // true

Optional types

You have the optional of adding static types to your variable declarations.

String name = 'Bob';

Adding types is a good way to clearly express your intent. Tools like compilers and editors can use these types to help you, by providing early warnings for bugs and code completion.

final

If you never intend to change a variable, use final instead of var or in addition to a type. Once a final variable is set, it can't be changed.

final String name = 'Bob';
name = 'Alice'; // Compile ERROR (VM or to JavaScript)

Summary

Dart variables are optionally typed, though we generally recommend using types. Variables can be marked as final, locking the value. Unitialized variables have an initial value of null.

Built-in types

The Dart language has special support for the following types:

You can initialize an object of any of these special types using a literal. For example, 'this is a string' is a string literal, and true is a boolean literal.

Because every variable in Dart is an object—an instance of a class—you can usually use constructors to create variables. Variables for the built-in types are no different. For example, you can use the Map() constructor to create a map, using the code new Map().

Strings

A Dart string is a sequence of Unicode character codes. You can use either single or double quotes to create a string:

var s1 = 'Single quotes work well for string literals.';
var s2 = "Double quotes work just as well.";
var s3 = 'It\'s easy to escape the string delimiter.';
var s4 = "It's even easier to just use the other string delimiter.";

You can put the value of an expression inside a string by using ${expression}. If the expression is a variable, you can skip the {}.

var s = 'string interpolation';

print('Dart has $s, which is very handy.');
print('That deserves all caps. ${s.toUpperCase()} is very handy!');

To create a multi-line string, you can use a triple quote with either single or double quotation marks.

var s1 = '''
You can create
multi-line strings like this one.
''';

var s2 = """This is also a
multi-line string.""";

You can create a "raw" string by prefixing it with @.

var s = @'In a raw string, even \n is ignored.';

As for all objects, you can check whether two strings are equivalent (have the same characters) using the == operator:

var name = 'NAME';
var greeting = "Hello, $name!";
var greetingTemplate = 'Hello, NAME!';

print(greeting == greetingTemplate); //true; they have the same characters

String methods

Each string literal has the type String. String has some handy methods, including some that let you determine whether a string starts with, ends with, or contains another string.

var fullName = 'Cuthbert Musgrave Girdlestone, III';

fullName.startsWith('Cuthbert');            // true
fullName.endsWith('III');                   // true
fullName.contains(new RegExp('Musgrave'));  // true

Strings are immutable objects, which means you can create them but you can't change them. If you look closely at the String API docs, you'll notice that none of the methods actually changes the state of a String. For example, the method replaceAll() returns a new String without changing the original String.

var greetingTemplate = 'Hello, NAME!';
var greeting = greetingTemplate.replaceAll(new RegExp("NAME"), 'Bob');
print(greeting == greetingTemplate); // false; greetingTemplate didn't change

StringBuffer methods

To programmatically generate a string, you can use StringBuffer. A StringBuffer doesn't generate a new String object until toString() is called.

var sb = new StringBuffer();

sb.add("Use a StringBuffer");
sb.addAll(["for ", "efficient ", "string ", "creation "]);
sb.add("if you are ").add("building lots of strings.");

var fullString = sb.toString();

print(fullString); // Use a StringBuffer for efficient string creation
                   // if you are building lots of strings.

sb.clear();        // All gone!

Summary of strings

A String is an immutable sequence of 32-bit Unicode scalar character codes. String literals are delimited with either single quotes or double quotes. Strings can be multi-line with triple quotes, or "raw" and unfiltered with a @ prefix.

You can use string interpolation to insert variable or expression values into a string literal. Another option for building strings is StringBuffer.

Strings can do a lot more than we showed here. For example, you can split a string, create a substring, trim it, and lots more.

Numbers

Dart numbers come in two flavors:

int
Integers of arbitrary size
double
Decimal 64-bit doubles, as specified by the IEEE 754 standard

Both int and double are subinterfaces of num. The num interface defines basic operators such as +, -, /, and *, as well as bitwise operators such as >>. The num interface is also where you'll find abs(), ceil(), and floor(), among other methods. If num and its subinterfaces don't have what you're looking for, the Math class might.

Integers are numbers without a decimal point. Here are some examples of defining integer literals:

var x = 1;
var hex = 0xDEADBEEF;
var bigInt = 3465346583465243765923847659234765928347659567398475647495873984572947593470294387093493456870849216348723763945678236420938467345762304958724596873045876234572037862934765294365243652548673456705673465273465246734506873456729457623845623456234650457693475603768922346728346256;

If a number includes a decimal, it is a double. Here are some examples of defining double literals:

var y = 1.1;
var exponents = 1.42e5;

Here's how you turn a string into a number, or vice versa:

// string -> int
var one = Math.parseInt("1");                   // 1

// string -> double
var onePointOne = Math.parseDouble("1.1");      // 1.1

// int -> string
String oneAsString = 1.toString();              // "1"

// double -> string
String piAsString = 3.14159.toStringAsFixed(2); // "3.14"

Summary of numbers

Dart supplies a num interface with two subinterfaces: int and double. Integers are arbitrary precision, meaning they have arbitrary size. Doubles are 64-bit decimals that follow IEEE 754 spec. The Math class has constants such as Math.PI and utilities such as parseDouble().

Booleans

Dart has a formal boolean type, named bool. Only two objects have type bool: the boolean literals, true and false.

When Dart expects a boolean value, if that value isn't true, then it is false. Unlike in JavaScript, values such as 1 or non-null objects are not treated as true.

For example, consider the following code:

var name = 'Bob';
if (name) {
  print("You have a name!"); // Prints in JavaScript, not in Dart
}

In JavaScript, this code prints "You have a name!" because name is a non-null object. However, in Dart, the above doesn't print at all because name is converted to false because name != true.

Here's another example of code that behaves differently in JavaScript and Dart:

if (1) {
  print("JavaScript prints this line because it thinks 1 is true.");
} else {
  print("Dart prints this line because it thinks 1 is NOT true.");
}

Dart's treatment of booleans is designed to avoid the strange behaviors that can arise when many values can be treated as true. What this means for you is that, instead of using code like if (nonboolean_value), you should instead explicitly check for values. For example:

// Check for an empty string.
var fullName = '';
if (fullName.isEmpty()) {
  print("Please enter a full name");
}

// Check for zero.
var hitPoints = 0;
if (hitPoints == 0) {
  print("Uh oh! Looks like you died.");
}

// Check for null.
var unicorn = null;
if (unicorn == null) {
  print("You didn't wish hard enough. Wish harder.");
}

// Check for NaN.
var iMeantToDoThis = 0/0;
if (iMeantToDoThis.isNaN()) {
  print("0/0 is not a number.");
}

Summary of booleans

Dart has a true boolean type named bool, with only two values: true and false. Due to Dart's boolean conversion rules, all values except true are converted to false.

Lists (also known as arrays)

Perhaps the most common collection in nearly every programming language is the array, or ordered set of objects. In Dart, arrays are List objects, so we usually just call them lists. When you compile Dart to JavaScript, a Dart list compiles to a JavaScript array.

Dart list literals look like JavaScript array literals. Here's a simple Dart list:

var list = [1,2,3];

You can get a list's length and refer to list elements just as you would in JavaScript:

var list = [1,2,3];
print(list.length); // the # of elements: 3
print(list[1]);     // the second item: 2

You can add an element to a list using the add() method:

var list = [1,2,3];
list.add(4);

To remove elements from a list (reducing the list's size), use the removeRange() method:

var list = [1,2,3,4];
list.removeRange(2, 1); // remove the third element

So far, we've only talked about extendable lists, but you can also create lists with a fixed size, using the List() constructor:

var list = new List(3); //fixed-size list (full of null values)

Iterating

When you need to work on each element of a list, you can use for, for...in, or forEach(). Use for when you need the current iteration index:

var list = [1,2,3];
for (var x = 0; x < list.length; x++) {
  print('$x: ${list[x]}');
}

If you don't need the index, you can use for...in:

var list = [1,2,3];
for (final x in list) {
  print(x);
}

If you just want to apply a function to each element of the list, use the forEach() method:

var list = [1,2,3];
void printElement(element) => print(element);
list.forEach(printElement);

Or, more succintly:

var list = [1,2,3];
list.forEach((element) => print(element));

List and Collection methods

The forEach() method is just one of many handy methods defined by the List interface and its superinterface, Collection. For example, the filter() method returns a new collection with only the elements that satisfy a condition. The every() and some() methods check whether a collection matches every condition or at least one condition, respectively. The sort() method lets you sort a list using any criteria you like.

Summary of lists

Lists are ordered sequences of objects. Dart has no class or interface called Array, but lists act extremely similar to the arrays that many programming languages have. To find the methods you can invoke on lists, see the documentation for List and its superinterface, Collection.

Maps

In general, a map is an object that associates keys to values. Dart support for maps is provided by map literals and the Map interface.

Here's a simple Dart map:

var gifts = {                         // a map literal
// keys       values
  "first"  : "partridge",
  "second" : "turtledoves",
  "fifth"  : "golden rings"};

In map literals, each key must be a string. If you use a Map constructor, then you have more options: the key can be a string, a number, or any other object that implements the Hashable interface.

var map = new Map();                  // use a Map constructor
map[1] = "partridge";                 // key is 1; value is "partridge"
map[2] = "turtledoves";               // key is 2; value is "turtledoves"
map[5] = "golden rings";              // key is 5; value is "golden rings"

A map value can be any object or null.

You add a new key-value pair to an existing map just as you would in JavaScript:

var gifts = { "first": "partridge" };
gifts["fourth"] = "calling birds";    // add a key-value pair

You retrieve a value from a map the same way you would in JavaScript:

var gifts = { "first": "partridge" };
print(gifts['first']);                // partridge

If you look for a key that isn't in a map, you get a null in return. However, because values can be null, you might need to use a method such as containsKey() or putIfAbsent() to make sure you interpret null correctly.

var gifts = { "first": "partridge" };
print(gifts['fifth']);                // null

Use .length to get the number of key-value pairs in the map:

var gifts = { "first": "partridge" };
gifts["fourth"] = "calling birds";
print(gifts.length);                  // 2

To remove a key-value pair from a map, use the remove() method:

var gifts = { "first": "partridge" };
gifts["fourth"] = "calling birds";
gifts.remove('first');
print(gifts.length);                  // 1
print(gifts['first']);                // null

You can copy a map using the Map.from() constructor:

var gifts = { "first": "partridge" };
var regifts = new Map.from(gifts);
print(regifts['first']);              // partridge

Iterating

You have a few choices for iterating through the contents of a map. Using the forEach() method gives you access to both the key and the value.

var gifts = {
  "first" : "partridge",
  "second": "turtledoves",
  "fifth" : "golden rings"};
gifts.forEach((k,v) => print('$k : $v'));

If you are interested in just the keys or just the values, use getKeys() or getValues(), respectively. Both methods return a Collection object.

var gifts = {"first": "partridge", "second": "turtledoves"};
var values = gifts.getValues();
values.forEach((v) => print(v));      //partridge, turtledoves

Summary of maps

Map literals and the Map interface let you map keys to values. If you use a literal to create a map, the keys must be strings.

You'll learn more about maps in the Parameterized types (aka generics) section.

Summary of built-in types

Dart's built-in types all have special literals and implement a built-in interface. For example, numbers have literals such as 1 and 1.1, and they implement the num interface.

You often use literals to create objects of most built-in types, but you can also use constructors. Booleans are unusual because you can't create new objects of type bool; you're stuck with true and false.

Functions

Here's a simple function:

String say(String from, String msg) => "$from says $msg";

And here's an example of calling it:

print(say("Bob", "Hello")); // "Bob says Hello"

Omitting the types, you could write the above as:

say(from, msg) => "$from says $msg";

However, we recommend using types for function signatures.

The => e; syntax is a shorthand for { return e; }. For example, say(from, msg) => "$from says $msg"; is the same as:

say(from, msg) {
  return "$from says $msg";
}

Optional parameters

Wrapping a function parameter in [] marks it as an optional parameter.

String say(String from, String msg, [String device]) {
  var result = "$from says $msg";
  if (device != null) {
    result = "$result with a $device";
  }
  return result;
}

Here's an example of calling this function without the optional parameter:

print(say("Bob", "Howdy")); // Bob says Howdy

Here's an example of calling this function with the third parameter:

print(say("Bob", "Howdy", "smoke signal"));
// Bob says Howdy with a smoke signal

Default values for optional parameters

Optional parameters may have default values. The default values must be compile time constants. If no default value is provided, the value is null (as we saw above).

String say(String from, String msg, [String device='carrier pigeon']) {
  var result = "$from says $msg";
  if (device != null) {
    result = "$result with a $device";
  }
  return result;
}

Omitting the optional parameter, you can see how the default value is used:

print(say("Bob", "Howdy")); // Bob says Howdy with a carrier pigeon

Named parameters

Optional parameters are also named parameters.

print(say("Bob", "Howdy", device: "tin can and string"));
// Bob says Howdy with a tin can and string

First class functions

You can pass a function as a parameter to another function. For example:

List ages = [1,4,5,7,10,14,21];
List oddAges = ages.filter((i) => i % 2 == 1);

Which is the same as:

bool isOdd(num i) => i % 2 == 1;
List ages = [1,4,5,7,10,14,21];
List oddAges = ages.filter(isOdd);

You can also assign a function to a variable, such as:

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
print(loudify('hello'));

Lexical closures

Functions can close over variables defined in surrounding scopes. The below example shows how makeAdder captures the variable n and makes it available to the function that makeAdder returns. Whereever the returned function goes, it remembers n.

Function makeAdder(num n) {
  return (num i) => n + i;
}

main() {
  var add2 = makeAdder(2);
  print(add2(3)); // 5
}

(Special thanks to Bob Nystrom for this example.)

Return values

All functions return a value. If no return value is specified, the statement return null; is implicitly appended to the function body.

Summary of functions

Dart supports first class functions, with optional parameters, named parameters, and default values for parameters. Functions may be assigned to variables and passed as parameters to other functions. Functions also support lexical closures, which allows access to variables outside its immediate lexical scope.

Operators

Dart does operators. Not only does it define them, but it lets you redefine many of them.

The following table shows all of Dart's operators, in order of precedence.

Description Operator
unary postfix expr++ expr-- () [] .
unary prefix -expr !expr ~expr ++expr --expr
multiplicative * / % ~/
additive + -
shift << >>
relational is is! >= > <= <
equality == != === !==
bitwise AND &
bitwise XOR ^
bitwise OR |
logical AND &&
logical OR ||
conditional expr ? expr : expr
assignment = *= /= ~/= %= += -= <<= >>= &= ^= |=

For example, the % operator has higher precedence than (and thus executes before) the == operator, which has higher precedence than the && operator. That precedence means that the following two lines of code execute the same way:

if ((n % i == 0) && (d % i == 0)) //parens improve readability
if (n % i == 0 && d % i == 0)     //harder to read, but equivalent

This section covers the following topics:

Arithmetic operators

Dart supports the usual arithmetic operators.

Operator Meaning
+ add
subtract
-expr unary negation (reverse the sign of the expression)
* multiply
/ divide
~/ divide, returning an integer result
% get the remainder

Example:

int a = 2;
int b = 3;

print('${a + b}');  //5
print('${a - b}');  //-1
print('${a * b}');  //6
print('${a / b}');  //0.6666666666666666
print('${a ~/ b}'); //0 (quotient)
print('${a % b}');  //2 (remainder)

Dart also supports both prefix and postfix increment and decrement operators.

Operator Meaning
++var var = var + 1 (expression value is var + 1)
var++ var = var + 1 (expression value is var)
--var var = var – 1 (expression value is var – 1)
var-- var = var – 1 (expression value is var)

Example:

int a = 2;

print('${ ++a }'); //3 (increment before returning a value)
print('${ a++ }'); //3 (increment after returning a value)
print('${ a-- }'); //4 (decrement after returning a value)
print('${ --a }'); //2 (decrement before returning a value)

Equality and relational operators

Operator Meaning
== equal (see discussion below)
!= not equal
=== same instance
!== not the same instance
> greater than
< less than
>= greater than or equal to
<= less than or equal to
is true if the object has the specified type (see discussion below)
is! false if the object has the specified type

To test whether two objects x and y represent the same thing, use the == operator. You don't usually need to use the === operator, which tests whether two objects are, in fact, the exact same object. Here's how the == operator will work:

  1. If x===y, return true.
  2. Otherwise, if either x or y is null, return false.
  3. Otherwise, return the result of x.equals(y).

The is and is! operators are handy for checking types. The result of obj is T is true if obj implements the interface specified by T. For example, obj is Object is always true.

Here's an example of using each of the equality and relational operators:

int a = 2;
int b = 3;
int c = a;

print(a == 2);       //true; 2 and 2 are equal
print(a != b);       //true; 2 and 3 aren't equal
print(a === c);      //true; a and c are the same object
print(a !== b);      //true; 2 and 3 aren't the same object
print(b > a);        //true; 3 is more than 2
print(a < b);        //true; 2 is less then 3
print(b >= b);       //true; 3 is greater than or equal to 3
print(a <= b);       //true; 2 is less than or equal to 3
print(a is num);     //true; 2 is a number
print(a is! String); //true; 2 is an int, not a string

Assignment operators

You assign values using the = operator. You can also use compound assignment operators, which combine an operation with an assignment.

Compound assignment Equivalent expression
For an operator op: a op= b a = a op b
Example: a += b a = a + b

Here's a full list of the assignment operators:

=
+=
–=
*=
/=
~/=
%=
<<=
>>=
&=
^=
|=

The following example uses both assignment and compound assignment operators:

int a = 2;           //Assign using =

a *= 3;              //Assign and multiply: a = a * 3
print('a *= 3: $a'); //a *= 3: 6

Logical operators

You can invert or combine boolean expressions using the logical operators.

Operator Meaning
!expr inverts the following expression (changes false to true, and vice versa)
|| logical OR
&& logical AND
if (!done && (col == 0 || col == 3)) {
  //...do something
}

Bitwise and shift operators

You can manipulate the individual bits of objects in Dart. Usually, you'd use these operators with integers.

Operator Meaning
& AND
| OR
^ XOR
~expr unary bitwise complement (0s become 1s; 1s become 0s)
<< shift left
>> shift right

Here's an example of using bitwise and shift operators.

int value = 0x22;
int bitmask = 0x0F;

print(value);                                //34 (0x22)
print(value.toRadixString(16));              //22
print(value.toRadixString(2));               //100010
print((value & bitmask).toRadixString(16));  //2  (AND)
print((value & ~bitmask).toRadixString(16)); //20 (AND NOT)
print((value | bitmask).toRadixString(16));  //2f (OR)
print((value ^ bitmask).toRadixString(16));  //2d (XOR)
print((value << 4).toRadixString(16));       //220
print((value >> 4).toRadixString(16));       //2

Other operators

Operator Name Meaning
() function application represents a function call
[] list access refers to the value at the specified index in the list
expr1 ? expr2 : expr3 conditional if expr is true, executes expr; otherwise, executes expr (technically special syntax, not an operator)
. member access refers to a property of an expression; example: foo.bar selects property bar from expression foo

Operators are methods

Operators are just instance methods with special names. For example, the expression 1 + 2 invokes the + method on 1, with the argument 2—something like 1.+(2). This has a couple of consequences:

  • Dart lets you override many operators. For example, if you define a Vector class, you might define a + method to add two vectors.
  • For operators that work on two operands, the leftmost operand determines which version of the operator is used. For example, if you define a Vector class and a Point class, aVector + aPoint uses the Vector version of +.

The following operators can be overridden:

<
>
<=
>=

+
/
~/
*
%
|
^
&
<<
>>
[]
[]= (list assignment)
~
equals() (==) *

Summary of operators

Dart operators should look and act familiar. Behind the scenes, an operator is a specially named method that's invoked on its first operand. As a result, operand order can make a difference: a+b might not give the same result as b+a. You can override many operators.

Control flow

You can control the flow of your Dart code using any of the following:

If and else

if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}

Remember, unlike JavaScript, Dart treats all values that are not true as false. See Booleans for more info.

For loops

You can iterate with the standard for loop.

for (int i = 0; i < candidates.length; i++) {
  candidates[i].interview();
}

Closures inside of Dart's for loops correctly capture the value of the index, avoiding a common pitfall found in JavaScript. For example, consider:

main() {
  var callbacks = [];
  for (var i = 0; i < 2; i++) {
    callbacks.add(() => print(i));
  }
  callbacks.forEach((c) => c());
}

The output is 0 and then 1, as expected. In contrast, the example would print 2 and then 2 in JavaScript.

If the object that you are iterating over is a Collection, you can use the forEach() method. Using forEach() is a good option if you don't need to know the current iteration counter.

candidates.forEach((candidate) => candidate.interview());

Collections also support the for-in form of iteration:

var collection = [0, 1, 2];
for (var x in collection) {
  print(x);
}
// prints:
// 0
// 1
// 2

While and do while

A while loop evaluates the conditional before the loop.

while (!auctionItem.currentWinner(bidder) &&
       auctionItem.currentBid < bidder.maximumBid) {
  auctionItem.placeBid(bidder, auction.currentBid + 1);
}

A do while loop evaluates the conditional after the loop.

do {
  printLine();
} while (!atEndOfPage());

Break and continue

Use break to stop looping.

while (true) {
  if (shutDownRequested()) break;
  processIncomingRequests();
}

Use continue to skip to the next loop iteration.

for (int i = 0; i < candidates.length; i++) {
  var candidate = candidates[i];
  if (candidate.yearsExperience < 5) {
    continue;
  }
  candidate.interview();
}

You might write that example differently if you're using a Collection.

candidates.filter((c) => c.yearsExperience >= 5)
          .forEach((c) => c.interview());

Switch and case

Switch statements in Dart compare objects using ==. Remember to include a break statement at the end of each non-empty case clause to avoid fall-through (which is an error, see below). A default clause can be used to catch conditions that don't match.

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClose();
    break;
  case 'PENDING':
    executePending();
    break;
  case 'APPROVED':
    executeApproved();
    break;
  case 'DENIED':
    executeDenied();
    break;
  case 'OPEN':
    executeOpen();
    break;
  default:
    executeUnknown();
}

The following example omits the break statement in the case clause, thus generating an error:

var command = 'OPEN';
switch (command) {

  case 'OPEN':
    executeOpen();
    // ERROR: missing break causes an exception to be thrown!!

  case 'CLOSED':
    executeClose();
    break;
}

However, Dart does support empty case clauses, allowing a form of fall-through.

var command = 'CLOSED';
switch (command) {
  case 'CLOSED':     // empty case falls through
  case 'NOW_CLOSED':
    // runs for both CLOSED and NOW_CLOSED
    executeClose();
    break;
}

Exceptions

Your Dart code can throw and catch exceptions. Exceptions are errors that signal something happened that was not anticipated. If not caught, exceptions bubble up to the top of the program.

In contrast to Java, all of Dart's exceptions are unchecked exceptions. Methods do not declare which exceptions they might throw, and you are not required to catch any exceptions.

Dart provides an Exception interface and numerous predefined exception types. You can, of course, define your own exceptions by extending the Exception interface. Some examples of common exceptions include:

However, Dart programs can throw any object as an exception.

Throw

Here's how you throw, or raise, an exception.

throw new IllegalArgumentException('Value must be greater than zero');

You can also throw arbitrary objects.

throw "Out of llamas!";

Catch

Catching, or capturing, an exception stops the exception from propagated. Catching an exception gives you a chance to handle it.

try {
  breedMoreLlamas();
} catch (final OutOfLlamasException e) {
  buyMoreLlamas();
}

To handle code that can throw more than one type of exception, you can specify multiple catch clauses. The first catch clause that matches the thrown object's type handles the exception. If the catch clause does not specify a type, that clause can handle any type of thrown object.

try {
  breedMoreLlamas();
} catch (final OutOfLlamasException e) {  // a specific exception
  buyMoreLlamas();
} catch (final Exception e) {             // anything that is an exception
  print("Unknown exception: $e");
} catch (final e) {                       // no specified type, handles all
  print("Something really unknown: $e");
}

You can match more than one exception in a single catch clause.

try {
  breedMoreLlamas();
} catch (final OutOfLlamasException oole,
        final NoFemaleLlamasException nfle) {
  print('Out of luck');
}

Finally

To ensure that some code runs whether or not an exception is thrown, use the finally clause.

If no catch clause matches the exception, the finally clause runs and then the exception is propagated.

try {
  breedMoreLlamas();
} finally {
  cleanLlamaStalls();  // always run, even if exception is thrown
}

The finally clause runs after any matching catch clauses.

try {
  breedMoreLlamas();
} catch (final e) {
  print("Error: $e");  // handle exception first
} finally {
  cleanLlamaStalls();  // then run finally
}