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
- A basic Dart program
- Variables
- Built-in types
- Functions
- Operators
- Control flow
- Exceptions
- 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:
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
- equality and relational operators
- assignment operators
- logical operators
- bitwise and shift operators
- other operators
- operators as methods
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:
- If
x===y
, return true. - Otherwise, if either x or y is null, return false.
- 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() (==
) *
* The == operator can currently be overridden, but not for long. Soon the way to customize the behavior of == will be by overriding the equals() method.
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 }