In Dart, what is the difference between declaring a variable final and declaring it const?
answer
- set once versus known at compile time
- const is implicitly final
- DateTime.now() fits only one
- class level: static const
- instance fields: final, never const
basics
~20 sIn Dart, final means a variable can be assigned only once, with a value that may be computed at runtime; const means its value is a compile-time constant, deeply immutable and canonicalized. Every const variable is implicitly final.
solid answer
~40 s`final` is a single-assignment rule on the variable: `final now = DateTime.now();` is fine because the value is computed when the code runs, but assigning `now` again is a compile error. `const` is stronger: the value must be a **compile-time constant**, such as a literal, another constant or arithmetic on constant numbers, so `const now = DateTime.now();` does not compile. A const variable is implicitly final, and the constant object itself is deeply immutable and canonicalized, meaning equal constants share one instance. In a class, instance fields can be `final` but never `const`; a class-level constant is written `static const`. In a unit-conversion library, `const metersPerMile = 1609.344;` is a natural const, while a converter's `final String locale;` field is final because it differs per instance.
code
dart · 17 linesconst metersPerMile = 1609.344; // compile-time constant
const metersPerFoot = 0.3048;
const feetPerMile = metersPerMile / metersPerFoot; // const arithmetic
class Converter {
static const metersPerInch = 0.0254; // class-level constant
final String locale; // per-instance, set once
Converter(this.locale);
}
void main() {
final loadedAt = DateTime.now(); // runtime value, set once
// loadedAt = DateTime.now(); // error: can only be set once
// const now = DateTime.now(); // error: not a constant expression
print('$feetPerMile ft per mile, loaded at $loadedAt');
print(Converter('en').locale);
}go deeper
Recall that final is assign-once at runtime and const is a compile-time constant, and that const implies final.
Explain why instance fields cannot be const, what counts as a constant expression, and that final restricts only the variable.
Choose const for definitional data and final for computed values in library code, and use lints to keep the choice consistent.
Set team conventions on const, final and var so APIs signal intent clearly without slowing reviews over style.
## Two keywords, two different promises Dart has three ways to declare a variable: `var` (or a type) for a variable you may reassign, `final` for one you assign once, and `const` for one whose value is fixed when the program is compiled. `final` and `const` are both used when you never intend to change a variable, and dart.dev notes that **const variables are implicitly final**. The difference is *when* the value is known and *how much* is frozen. | | `final` | `const` | |---|---|---| | Assigned | Exactly once | Exactly once, at the declaration | | Value known | At runtime | At compile time | | Example value | `DateTime.now()`, a parsed config | `1609.344`, `'km'`, `2 * metersPerFoot` | | Object mutability | The referenced object may be mutable | The constant object is deeply immutable | | Instances | A new object each time the code runs | Equal constants share one canonical instance | | Instance field | Allowed | Not allowed; use `static const` | ## final: a single-assignment variable A `final` variable can be set only once. The value can come from anything: a function call, user input, the current time. After that, assigning again is a compile-time error (the analyzer reports `assignment_to_final_local` for a local). A final local does not even need its initializer on the same line, as long as Dart's flow analysis can see it is assigned exactly once before use. Key point: `final` restricts the **variable**, not the object. A `final` list can still have items added; only the variable cannot be pointed at a different list. ## const: a compile-time constant `const` requires the initializer to be a **constant expression**, one the compiler can evaluate: - number, string, boolean and `null` literals; - other `const` variables; - arithmetic and comparisons on constant numbers, like `1.01325 * bar`; - `is` and `as`, collection `if` and spreads inside constant collections; - calls to `const` constructors with constant arguments. A function call such as `DateTime.now()` or `double.parse('3.2')` is not a constant expression, so `const x = DateTime.now();` fails with `const_initialized_with_non_constant_value`. Because the value is computed once by the compiler, the resulting object is **deeply immutable** and **canonicalized**: two occurrences of the same constant value refer to a single instance. ## const in classes 1. An **instance field** cannot be `const`; the analyzer reports `const_instance_field` ("Only static fields can be declared as const"). Each instance may hold a different value, which contradicts a compile-time constant. 2. A constant that belongs to the class is written **`static const`**, for example `static const metersPerInch = 0.0254;`. 3. Instance fields that never change after construction are `final`. ## Choosing between them For a unit-conversion library the rule falls out naturally: - Conversion factors known by definition (`1609.344` meters per mile, `0.3048` meters per foot) are `const`, at top level or as `static const`. - Values computed at runtime, such as the time a table was loaded or a locale passed to a constructor, are `final`. - Loop counters and accumulators that change are `var`. Effective Dart's `prefer_const_declarations` lint nudges `final` declarations that could be `const` towards `const`, and `prefer_final_locals` nudges never-reassigned locals from `var` to `final`. Neither lint is in the default recommended set, but both show the intended direction. ## A checklist for the interview answer 1. Say **when** the value is known: runtime for `final`, compile time for `const`. 2. Say that `const` **implies** `final`, never the reverse. 3. Say **how deep** each goes: `final` freezes the variable, `const` freezes the whole object graph. 4. Say where each is **allowed**: `const` at top level, as a local, or as `static const`; `final` anywhere, including instance fields. 5. Mention **canonicalization** as the bonus: equal constants are one shared object. Covering these five points in two or three sentences is what separates a memorized definition from an understanding of the model. ## Common confusions - "`final` makes the object immutable": no, only the binding. - "`const` and `final` are interchangeable for fields": no, instance fields cannot be `const`. - "A `const` value can be computed at startup": no, it must be computable by the compiler.
- Why can't a Dart instance field be const?A const value is fixed at compile time and shared, while an instance field can hold a different value per object. Dart therefore allows only `static const` fields; the analyzer reports `const_instance_field` otherwise. Per-instance values that never change are declared `final` and set in the constructor.
- Can a Dart final local be declared without an initializer?Yes. `final String unit;` is valid if flow analysis can prove it is assigned exactly once before it is read, for example once in each branch of an if/else. Assigning it twice, or reading it before assignment, is a compile error.
saying these in an interview costs you the question
- final makes the referenced object immutable, so a final list cannot grow
- const values can be computed at startup, e.g. from DateTime.now()
- Instance fields can be declared const if they never change
- final and const behave identically; const is just a stricter style
- A const variable can be reassigned if it has no explicit type