skip to content

In Dart, what is the difference between declaring a variable final and declaring it const?

level: juniorimportance: must knowfreq 82%

answer

  1. set once versus known at compile time
  2. const is implicitly final
  3. DateTime.now() fits only one
  4. class level: static const
  5. instance fields: final, never const

basics

~20 s

In 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 lines
dart
const 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

for a junior

Recall that final is assign-once at runtime and const is a compile-time constant, and that const implies final.

for a middle

Explain why instance fields cannot be const, what counts as a constant expression, and that final restricts only the variable.

for a senior

Choose const for definitional data and final for computed values in library code, and use lints to keep the choice consistent.

for a principal

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