Classes in Dart, Part One

Dart is a familiar object oriented programming language, with classes, single inheritance, and interfaces. You can build classes and instantiate objects pretty much like how you would expect, with a few caveats and pleasant surprises. This post is the first in a series of posts that will explore classes in Dart.

If you think you need to use classes when programming in Dart, think again. Dart is a scripting language, which implies you can write "short and sweet" scripts and programs with ease. Dart functions very well with only, well, er, functions, alleviating the requirement to build classes. In fact, I've made it through eleven posts here in this series without talking about classes. You can get far with just functions in Dart.

Dart is built to be scalable, helping you as your program grows in scope. You may start with a collection of functions, but as complexity increases, eventually you'll want to organize your code into classes.

Class

Straight from the language spec:

A class has constructors, instance members and static members. The instance members of a class are its instance methods, getters, setters and instance variables. The static members of a class are its static methods, getters, setters and static variables. Every class has a single superclass except class Object which has no superclass.

Translated, the above says:

Dart's classes have single inheritance

Dart's classes have constructors

Dart's classes have instance members like methods, getters, setters, and variables

Dart's classes can also have static methods, getters, setters, and variables

Single inheritance means each class has a single superclass. A class may only inherit from one other class.

Constructors are special methods on a class that initialize a new instance of a class. Constructors don't return anything. Dart supports traditional constructors (methods named the same as the class) and "named constructors". We will see examples of both shortly.

Instance members like instance methods and instance variables can manipulate the state of the object, and define the state of an object, respectively. An "instance" or "object instance" or "object" is a real, true instantiation of the class. Your normal interaction with classes is to instantiate, or create, a new instance of the class. This new instantiation is an object.

Static members, on the other hand, are methods and state of the class itself. Sometimes, it's helpful to define special behavior that doesn't require an object instance.

Hopefully this all sounds familiar. If you are new to class based object oriented languages, try to think about classes as blueprints for the things in your program. Each thing, or object, has state (its properties) and behavior or abilities (its methods).

Fields

A simple Dart class is defined like this:

class InputField {

int maxLength;

String name;

}

This class, named InputField, has two fields: maxLength and name. These fields define the state of the object, or what data it can hold.

The default value of fields and variables is null. You may only assign a const (aka compile-time constant) value to a field initialized in the body of a class. For example:

class InputField {

int maxSize = 20; // OK! a literal num is a constant

String name;

}

You may not use a non-const value like this:

class InputField {

int maxSize = 20;

String name;

// ERROR: expecting const expression

Date initializeAt = new Date();

}

Dart is designed to support consistent initialization of objects. One cannot observe an object before its initialization is complete. To achieve that, the preferred way to initialize instance variables in Dart is via the constructor's initializer, where the parameters to the constructor are available and access to this is restricted. Initializing variables at the point of declaration was added as a convenience for simple cases.

NOTE: there is a discussion to simplify the syntax to allow you to set a non-const value to a field in the body of a class (like Date initializeAt = new Date()), by transparently moving the initialization down into the constructor. Stand by for more news on this.

An implicit getter is generated for each field. If the field is non-final, an implicit setter is also generated for each field.

For example, if you add a field like int maxSize, the following will be automatically added to the class:

int get maxSize() => this.maxSize;

void set maxSize(int x) => this.maxSize = x;

Warning: it is a compile time error to manually declare a getter or setter with the same name as a field or method!

Create an object, accessing fields

Once you have a class defined, you can create new instances:

InputField field = new InputField();

Setting and getting fields, via the automatically provided setters and getters, is easy:

field.maxSize = 30;

field.name = 'first-name';

print(field.name); // 'first-name'

This, of course, means that you can't create truly private fields for a class, because any field you define will receive a corresponding getting and (unless the field is final) a setter.

We have not yet discussed the visibility rules for Dart yet, because we have not introduced libraries (which create the boundaries for any private fields or methods.)

Constructors

As you saw in the code above, if you do not specify a constructor, a default constructor will be provided.

You can also specify your own constructors. For example:

class InputField {

int maxSize;

String name;

InputField(this.maxLength, this.name);

}

This constructor that takes two parameters, and uses a cool Dart syntax shortcut to set the field values by matching the names of the parameters to the fields.

The above example constructor is equivalent to:

InputField(maxSize, name) {

this.maxSize = maxSize;

this.name = name;

}

But of course, that's the long way to do it. You can also mix the shorthand with a full constructor like this:

InputField(this.maxSize, this.name) {

renderComponent();

}

If you define a constructor, no default zero-parameter constructor will be provided. This means, given this class definition...

class InputField {

int maxSize;

String name;

InputField(this.maxLength, this.name);

}

... you will be unable to create a new object like this:

var field = new InputField(); // COMPILE TIME ERROR: no matching constructor

Dart does not allow for constructor overloading, which means you cannot have two constructors of the same name defined for the same class. Luckily, thanks to Dart's optional method arguments, this is less of a problem than you might think.

For example, using both optional arguments and default argument values:

class InputField {

int maxSize;

String name;

InputField([this.type, this.maxSize = 20]);

}

The following instantiations will all work:

var field = new InputField(); // works!

field.name; // null

var field = new InputField(maxSize: 40); // works!

field.maxSize; // 40

var field = new InputField('first-name', 20); // works!

field.name; // first-name

field.maxSize; // 20

Named constructors

Another way to work around the lack of constructor overloading, as well as provide more clarity to your users of your class, is to use Dart's cool named constructors.

class InputField {

int maxSize;

String name;

InputField([this.name, this.maxSize = 20]);

InputField.Small(this.name) {

this.maxSize = 10;

}

}

You can instantiate an object using a named constructor like this:

InputField field = new InputField.Small('middle-initial');

Summary

Dart is a class based object oriented programming language. Classes have fields defined in the body of the class, and the fields can be initialized with compile-time constants or by values passed in from the constructor. Dart will generate a getter for each field, and if the field is non-null, a setter will also be generated.

If you do not specify a constructor, a default constructor will be provided for you. You cannot overload the constructor, but you can use either named constructors or optional arguments to provide options for initialization.

There is a lot more to cover with classes, this just skims the surface! We'll continue with classes in our next post.

Popular posts from this blog

Now, this has to have a built-in somewhere in Scala , because it just seems too common. So, how to convert an Array to a List in Scala? Why do I need this? I needed to drop to Java for some functionality, which in this case returns an Array. I wanted to get that Array into a List to practice my functional programming skillz. **Update**: I figured out how to convert Arrays to Lists the Scala way. Turns out it's a piece of cake. val myList = List.fromArray(Array("one", "two", "three")) or val myList = Array("one","two","three").elements.toList The call to elements returns an Iterator , and from there you can convert to a List via toList . Nice. Because my first version wasn't actually tail recursive, what follows is a true tail recursive solution, if I were to implement this by hand. The above, built in mechanism is much better, though. object ArrayUtil { def toList[a](array: Array[a]): List[a] = { d

In which I port a snazzy little JavaScript audio web app to Dart , discover a bug, and high-five type annotations. Here's what I learned. [As it says in the header of this blog, I'm a seasoned Dart developer. However, I certainly don't write Dart every day (I wish!). Don't interpret this post as "Hi, I'm new to Dart". Instead, interpret this post as "I'm applying what I've been documenting."] This post analyzes two versions of the same app, both the original (JavaScript) version and the Dart version. The original version is a proxy for any small JavaScript app, there's nothing particularly special about the original version, which is why it made for a good example. This post discusses the differences between the two implementations: file organization, dependencies and modules, shims, classes, type annotations, event handling, calling multiple methods, asynchronous programming, animation, and interop with JavaScript libraries. F

In which the virtues of automated mechanical arboreal pruning are extolled over quaint manual labor, as applied to web development build processes. The setup Ever notice how the primary bit of marketing for many traditional web programming libraries is their download size? Why is that? Check this out: Why does size matter so much for these libraries? Your first instinct is probably, "because the more bytes you shuttle across the wire, the slower the app starts up." Yes, this is true. I'd also say you're wrong. The primary reason that size matters for these libraries is because traditional web development has no intelligent or automated way to prune unused code so you can ship only the code that is used over the wire. The web is full of links, yet web dev has no linker The web development workflow is missing a linking step. A linker's job is to combine distinct project files into a single executable. A smart linker will only incl