Classes
Classes expose functionality on how to construct a new instance of a requested object type, functionality to expose methods and data, and functionality that encapsulates variables to track object state within its scope.
Every object in Dart is an instance of a class. You’ve been using Dart’s built-in classes throughout this book. Some built-in classes that you’ve worked with so far are Map, String, and List.
Custom Classes
Dart supports single inheritance, meaning that if no superclass is defined, the superclass will default to class Object. Classes allow you to construct your own objects in a declarative fashion. Let’s create your first class:
EXAMPLE 4.5
class Airplane
{
String color = "Silver";
String wing = "Triangle";
int seatCount = 2;
double fuel = 100.50;
}
main() {
Airplane yourPlane = new Airplane();
Airplane myPlane = new Airplane();
print(myPlane.wing); //prints " Triangle "
print(myPlane.seatCount); //prints "2"
yourPlane.seatCount = 1;
print(yourPlane.seatCount); //prints "1"
}
Example 4.5 accomplished two things. First it defined a class named Airplane with some public fields. Next, in the main() function, it instantiated two new object instances of class type Airplane named yourPlane and myPlane.
Upon instantiation, the field values of color, fuel, seatCount, and wing for both yourPlane and myPlane have matching field values. This is because their field values are assigned default values, and both are created from the class Airplane.
The Airplane class exposes seatCount as a public integer, so you are able to modify the seatCount value by using dot notation to access yourPlane.seatCount. All public class fields can be accessed using the dot syntax.
Why did you modify the seatCount? Well, to make the plane lighter of course! Let’s add a method that will return the weight of the plane (Example 4.6).
EXAMPLE 4.6
class Airplane
{
String primaryColor = "Silver";
String wing = "Triangle";
int seatCount = 2;
double fuelCapacity = 100.50;
double getWeight() {
return 1000 + seatCount + fuelCapacity;
}
}
main() {
Airplane yourPlane = new Airplane();
Airplane myPlane = new Airplane();
yourPlane.seatCount = 1;
print( 'yourplane weight:'+ yourPlane.getWeight().toString() );
print( 'myplane weight: '+ myPlane.getWeight().toString() );
}
//Output:
//yourplane weight: 1101.5
//myplane weight: 1102.5
Inferred Namespace
Namespaces are not a language feature in Dart, but the concept exists. A namespace is an area in your program where a named identifier can be called with only the unique name, and no prefix, to access an object. Conceptually, in Dart, a namespace is the sum of all the inherited scopes.
The “Lexical Scope” section talked about how curly brackets delineate scope hierarchies. The combined output of these inherited scopes creates the active namespace.
If you look at the class statement, you’ll notice that all the Airplane fields are wrapped inside a new class scope named Airplane.
class Airplane
{
//declaration
//declaration
//declaration
//declaration
}
When you instantiate a new class, you create a new instance of the superclass Object, and declare additional class fields by wrapping them in the class’s top-level scope. The new fields are directly accessible by their named identifiers within the namespace of the class instance.
The caller in Example 4.6, main(), instantiates a new class instance and assigns it to local variable yourPlane. The variable’s named identifier has access to all the fields in the namespace of class Airplane. The main() function calls the print() function with an argument that calls yourPlane.getWeight(). When getWeight() executes, its function block is operating within the namespace delineated by class Airplane.
class Airplane
{
...
...
double getWeight() {
// inherits Airplane class scope and appends
// its new local function scope
}
}
In Chapter 5, you will see how to use libraries to control namespaces using the keywords show, hide, as, part, and part of.