Acho a resposta de Lasse no blog de Chris Storms uma ótima explicação.
Espero que eles não se importem que eu copie o conteúdo.
Esta é uma boa explicação dos campos finais, mas não explica realmente os construtores const. Nada nesses exemplos realmente usa que os construtores sejam construtores const. Qualquer classe pode ter campos finais, construtores const ou não.
Um campo no Dart é realmente um local de armazenamento anônimo combinado com um getter e setter criado automaticamente que lê e atualiza o armazenamento, e também pode ser inicializado em uma lista de inicializadores de construtor.
Um campo final é o mesmo, apenas sem o setter, portanto, a única maneira de definir seu valor é na lista de inicializadores do construtor e não há como alterar o valor depois disso - daí o "final".
O objetivo dos construtores const não é inicializar os campos finais, qualquer construtor generativo pode fazer isso. O objetivo é criar valores constantes de tempo de compilação: Objetos onde todos os valores de campo já são conhecidos em tempo de compilação, sem executar nenhuma instrução.
Isso impõe algumas restrições à classe e ao construtor. Um construtor const não pode ter um corpo (nenhuma instrução executada!) E sua classe não deve ter nenhum campo não final (o valor que "sabemos" em tempo de compilação não deve ser capaz de mudar posteriormente). A lista de inicializadores também deve inicializar campos para outras constantes de tempo de compilação, portanto, os lados direitos são limitados a "expressões constantes de tempo de compilação" [1]. E deve ser prefixado com "const" - caso contrário, você obtém apenas um construtor normal que satisfaz esses requisitos. Isso é perfeitamente normal, mas não é um construtor const.
Para usar um construtor const para realmente criar um objeto constante de tempo de compilação, você substitui "novo" por "const" em uma "nova" expressão. Você ainda pode usar "new" com um construtor const, e ele ainda criará um objeto, mas será apenas um novo objeto normal, não um valor constante de tempo de compilação. Isto é: Um construtor const também pode ser usado como um construtor normal para criar objetos em tempo de execução, bem como criar objetos constantes de tempo de compilação em tempo de compilação.
Então, como exemplo:
class Point {
static final Point ORIGIN = const Point(0, 0);
final int x;
final int y;
const Point(this.x, this.y);
Point.clone(Point other): x = other.x, y = other.y; //[2]
}
main() {
// Assign compile-time constant to p0.
Point p0 = Point.ORIGIN;
// Create new point using const constructor.
Point p1 = new Point(0, 0);
// Create new point using non-const constructor.
Point p2 = new Point.clone(p0);
// Assign (the same) compile-time constant to p3.
Point p3 = const Point(0, 0);
print(identical(p0, p1)); // false
print(identical(p0, p2)); // false
print(identical(p0, p3)); // true!
}
As constantes de tempo de compilação são canônicas. Isso significa que não importa quantas vezes você escreva "const Point (0,0)", você cria apenas um objeto. Isso pode ser útil - mas não tanto quanto parece, já que você pode simplesmente fazer uma variável const para manter o valor e usar a variável em seu lugar.
Então, para que servem as constantes de tempo de compilação?
- Eles são úteis para enums.
- Você pode usar valores constantes de tempo de compilação em casos de switch.
- Eles são usados como anotações.
As constantes de tempo de compilação costumavam ser mais importantes antes de o Dart mudar para as variáveis de inicialização lenta. Antes disso, você só podia declarar uma variável global inicializada como "var x = foo;" se "foo" for uma constante de tempo de compilação. Sem esse requisito, a maioria dos programas pode ser escrita sem usar nenhum objeto const
Portanto, um breve resumo: construtores Const são apenas para criar valores constantes de tempo de compilação.
/EU
[1] Ou realmente: "Potencialmente expressões constantes de tempo de compilação" porque também pode se referir aos parâmetros do construtor. [2] Então, sim, uma classe pode ter construtores const e não const ao mesmo tempo.