In Dart, how do fields, getters and setters relate in a class's interface, and why can a field become a getter without breaking callers?
answer
- a field is a getter plus setter
- final field: getter only
- interfaces hold accessors, not storage
- field can implement an abstract getter
- same call-site syntax either way
basics
~20 sEvery Dart field induces an implicit getter, plus a setter unless it is final, and interfaces contain those accessors rather than storage. So a field and a getter are interchangeable to callers, and either can implement or override the other.
solid answer
~50 sIn Dart, `rect.width` is always a getter call, whether `width` is a stored field or a computed `double get width => ...`. A field declaration induces an implicit getter and, unless it is `final`, a setter. A class's implicit interface therefore lists accessors, not storage. Three consequences follow. Callers never change when you turn a public field into a getter and setter pair, which is why Dart style starts with plain fields. An abstract `double get area;` can be implemented by a field `final double area;` as well as by a computed getter. And a subclass can override a getter with a field or a field with a getter; overriding a field with another field is legal but duplicates storage, which the `overridden_fields` lint flags. A getter's return type must be a subtype of its setter's parameter type.
go deeper
Recall that every field comes with an implicit getter, plus a setter unless it is final.
Explain why interfaces contain accessors, how a field can implement an abstract getter, and why final fields satisfy only getters.
Refactor fields into computed getters without breaking callers, watch for removed setters, and avoid field-over-field overrides.
Use uniform access to keep public APIs stable while storage strategies change behind them.
## Fields are accessors Dart follows the **uniform access principle**: code outside a class cannot tell whether a property is stored or computed. The mechanism is simple: - A field `double width = 0;` induces an implicit **getter** `width` and an implicit **setter** `width=`. - A `final` field induces only the **getter**. - An explicit `double get area => width * height;` is a getter with a body; `set area(double v) { ... }` is a setter. - Every read `r.width` is a getter invocation; every write `r.width = 3` is a setter invocation. A class's implicit interface therefore contains **getters and setters**, never raw storage. That is why fields show up among the members an implementing class must provide. ## Refactoring a field into a getter ```dart // Version 1 class Room { Room(this.width, this.depth); double width, depth; double area = 0; // stored, easy to get out of sync } // Version 2: same call sites, computed value class Room2 { Room2(this.width, this.depth); double width, depth; double get area => width * depth; } ``` Callers that read `room.area` compile unchanged. The Dart documentation makes the same point: you can start with instance variables and later wrap them in methods without changing client code. Two things **can** change: removing a setter (a computed `area` has none) breaks writers, and a getter that does heavy work surprises callers who assumed a cheap field read. ## Implementing and overriding across forms | Supertype declares | Subtype may provide | |---|---| | `double get area;` (abstract) | `final double area;`, `double area;` or `double get area => ...` | | `abstract double width;` | a non-final field `double width;` or a getter plus setter | | field `double width;` | override with a getter and setter pair | | `final double width;` | override with `double get width => ...` | The rules behind the table: 1. A `final` field satisfies a **getter** requirement only; if the supertype also requires a setter, you need a non-final field or an explicit setter. 2. Override rules apply to accessors like any member: a getter override may **narrow** the return type. 3. The getter's return type must be a subtype of the setter's parameter type (`getter_not_subtype_setter_types`). 4. Overriding a field with a field creates a second storage slot in the subclass; the superclass's own code keeps reading through the getter, so the two can diverge. The `overridden_fields` lint, in the recommended set, discourages this. ## In a floor-plan hierarchy ```dart abstract class Shape { double get area; } class MeasuredRoom implements Shape { MeasuredRoom(this.area); @override final double area; // a field implements the abstract getter } class Rect implements Shape { Rect(this.width, this.height); final double width, height; @override double get area => width * height; // a computed getter implements it } ``` Code summing a plan with `shapes.fold(0.0, (sum, s) => sum + s.area)` neither knows nor cares which form each shape uses. ## Details worth knowing - Compound assignment and `++` work on getter/setter pairs, and Dart calls the getter **exactly once**, storing the value in a temporary before calling the setter. - A getter-only property cannot be assigned: `rect.area = 3` is a compile error when there is no setter. - Private fields with public getters (`double _width; double get width => _width;`) are the idiomatic way to expose read-only state that the class itself mutates. Wrapping a public field in a trivial getter and setter pair adds nothing, which Dart style discourages.
- In Dart, can a final field implement an interface that declares both a getter and a setter for the same name?No. A `final` field induces only a getter, so the setter requirement remains unimplemented and the analyzer reports it. Use a non-final field, which induces both, or keep the `final` field for reading and add an explicit setter that does something meaningful.
- In Dart, why is overriding a field with another field discouraged?The subclass gets its own storage while the superclass's storage still exists. Code in the superclass that reads the field goes through the getter and sees the subclass value, but the superclass's constructor still wrote to its own slot, so state can diverge and memory is wasted. The `overridden_fields` lint flags it; override with a getter instead.
A property is a service counter: the customer asks for the area and gets an answer, never seeing whether a clerk pulled it from a drawer or measured the room on the spot.
saying these in an interview costs you the question
- Fields and getters are called with different syntax in Dart
- An abstract getter can only be implemented by another getter
- A final field provides both a getter and a setter
- Changing a public field to a getter forces every caller to add parentheses
- Interfaces in Dart include a field's storage, not just its accessors