Getters and Setters
Computing derived values and controlling how a field can be written, without changing how it's used from the outside.
読了時間 2 分
Getters and setters let a class expose something that looks exactly like a field from the outside, while actually running code every time it's read or written. Dart uses this a lot more than most languages, precisely because the syntax at the call site never changes even when you add logic later.
Getters: computed, read-only properties
class Rectangle {
double width;
double height;
Rectangle(this.width, this.height);
double get area => width * height;
double get perimeter => 2 * (width + height);
}
void main() {
final rect = Rectangle(4, 5);
print(rect.area); // 20.0
print(rect.perimeter); // 18.0
}Notice rect.area, not rect.area() — a getter is called like a plain field, with no parentheses, even though it's really running width * height every time. There's no stored area field at all; it's computed fresh on every access, which also means it's never out of sync with width and height the way a manually-cached value could be.
Setters: controlled writes
A setter runs custom logic whenever code assigns to a property, which is the natural place to add validation:
class Temperature {
double _celsius = 0;
double get celsius => _celsius;
set celsius(double value) {
if (value < -273.15) {
throw ArgumentError('Temperature cannot be below absolute zero');
}
_celsius = value;
}
double get fahrenheit => _celsius * 9 / 5 + 32;
}
void main() {
final temp = Temperature();
temp.celsius = 25;
print(temp.fahrenheit); // 77.0
temp.celsius = -300; // throws ArgumentError
}_celsius starts with an underscore, which makes it private to this library (roughly, this file) — Dart has no private keyword; the underscore prefix is the privacy mechanism. Outside code can only reach the value through get celsius and set celsius, so temp.celsius = -300 always goes through the validation in the setter — there's no way to bypass it and write _celsius directly from outside the class.
Why start with a plain field and add this later?
A common Dart pattern is starting with a plain public field, and only converting it to a private field plus getter/setter pair once you actually need validation or computed behavior:
// Before: a plain field
class Account {
double balance;
Account(this.balance);
}
// After: same external API, now with a rule enforced
class Account {
double _balance;
Account(this._balance);
double get balance => _balance;
set balance(double value) {
if (value < 0) throw ArgumentError('Balance cannot be negative');
_balance = value;
}
}Every call site using account.balance keeps working unchanged after this refactor — callers can't tell whether they're touching a real field or a getter/setter pair, and that's the entire point. It means you never have to guess up front whether a property will eventually need validation; you can start simple and add the rule later, without an API-breaking change.