Constructors, Fields, and Methods in Depth
When you create an object, you build it from three ingredients: fields (the data it holds), constructors (the recipe for making it), and methods (the things it can do). In the last chapter you met a simple class. Here we look at each part more closely.
Fields: An Object's Memory
A field is a variable that belongs to an object. Every object of the class gets its own copy.
class Player {
String name = 'unknown';
int score = 0;
}Two fields you will see often are final and late:
finalmeans the field is set once and never changes. Perfect for things like a birthday or an ID.latemeans "I promise to set this before using it." Dart checks you at runtime.
class Profile {
final String id; // must be set when created
late String nickname; // set later, before first use
Profile(this.id);
}Constructors: How Objects Are Born
A constructor is a special function that runs when you create an object with new-like syntax — in modern Dart, just the class name.
class Player {
final String name;
int score;
Player(this.name, {this.score = 0});
}
void main() {
final p = Player('Ada', score: 10);
print('${p.name}: ${p.score}');
}Three handy shorthand forms appear constantly:
Player(this.name)— an initializer list in the parameters. Thethis.namesets the field directly. Very common.- Named parameters in
{}— likescore: 10above. They can have default values and are optional unless markedrequired. - Named constructors — extra constructors with their own names, useful for special ways of building an object.
class Player {
final String name;
int score;
Player(this.name, {this.score = 0});
Player.anonymous() : name = 'anon', score = 0;
}
void main() {
final guest = Player.anonymous();
print(guest.name); // anon
}Methods: What Objects Can Do
A method is a function attached to a class. It can read and change the object's fields.
class Player {
final String name;
int score;
Player(this.name, {this.score = 0});
void addPoints(int points) {
score += points;
}
bool get isWinning => score > 100; // a getter: looks like a field
}A getter (the get keyword) is a computed value. Callers write p.isWinning with no parentheses, so it feels like a field but is calculated each time.
A Common Trap: Shadowing
If a parameter has the same name as a field, plain use of the name refers to the parameter, not the field. That is exactly why this.name = name (or the shorthand this.name) exists — this points to the object itself.
class Bad {
String name = '';
Bad(String name) {
name = name; // BUG: assigns the parameter to itself!
}
}Putting It Together
class Player {
final String name;
int score;
Player(this.name, {this.score = 0});
void addPoints(int points) => score += points;
bool get isWinning => score > 100;
@override
String toString() => '$name ($score points)';
}
void main() {
final p = Player('Ada');
p.addPoints(120);
print(p); // Ada (120 points)
print(p.isWinning); // true
}Fields store state, constructors shape it at birth, and methods act on it. With these three, you can model almost any real-world thing.
Exercise
Why does this.name = name work while name = name does not?
Show answer
Inside the constructor, name refers to the parameter, so name = name just copies the parameter onto itself. this.name explicitly points to the object's field, so this.name = name assigns the parameter's value to the field.
Exercise
What is the difference between a getter and a normal method?
Show answer
A getter is called without parentheses and reads like a field (p.isWinning), while a method is called with parentheses (p.addPoints(10)). Getters are best for cheap computed values; methods suggest an action that changes something.
Spotted a mistake, or code that does not work?