Я нахожу ответ Лассе в блоге Криса Стормса прекрасным объяснением.
Надеюсь, они не возражают, что я скопирую контент.
Это прекрасное объяснение полей final, но на самом деле оно не объясняет конструкторы const. Фактически ничто в этих примерах не использует то, что конструкторы являются конструкторами const. Любой класс может иметь поля final, конструкторы const или нет.
Поле в Dart на самом деле является анонимным хранилищем в сочетании с автоматически созданным геттером и сеттером, который считывает и обновляет хранилище, а также его можно инициализировать в списке инициализаторов конструктора.
Последнее поле такое же, только без установщика, поэтому единственный способ установить его значение - в списке инициализаторов конструктора, и нет возможности изменить значение после этого - отсюда и «final».
Суть конструкторов const не в том, чтобы инициализировать поля final, это может сделать любой генеративный конструктор. Дело в том, чтобы создать значения констант времени компиляции: объекты, в которых все значения полей известны уже во время компиляции, без выполнения каких-либо операторов.
Это накладывает некоторые ограничения на класс и конструктор. Конструктор const не может иметь тела (никаких выполняемых операторов!), А его класс не должен иметь каких-либо незавершенных полей (значение, которое мы «знаем» во время компиляции, не должно измениться позже). Список инициализаторов должен также инициализировать поля только другими константами времени компиляции, поэтому правая часть ограничена «выражениями констант времени компиляции» [1]. И он должен иметь префикс «const» - иначе вы просто получите нормальный конструктор, который удовлетворяет этим требованиям. Это прекрасно, просто это не константный конструктор.
Чтобы использовать конструктор const для фактического создания объекта-константы времени компиляции, вы затем заменяете «new» на «const» в выражении «new». Вы по-прежнему можете использовать "new" с константным конструктором, и он по-прежнему будет создавать объект, но это будет просто обычный новый объект, а не постоянное значение времени компиляции. То есть: конструктор const также может использоваться как обычный конструктор для создания объектов во время выполнения, а также для создания объектов-констант времени компиляции во время компиляции.
Итак, в качестве примера:
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!
}
Константы времени компиляции канонизированы. Это означает, что сколько бы раз вы ни писали «const Point (0,0)», вы создаете только один объект. Это может быть полезно, но не так сильно, как могло бы показаться, поскольку вы можете просто создать константную переменную для хранения значения и вместо нее использовать переменную.
Итак, для чего вообще нужны константы времени компиляции?
- Они полезны для перечислений.
- Вы можете использовать значения констант времени компиляции в случаях переключения.
- Они используются как аннотации.
Константы времени компиляции были более важными до того, как Дарт перешел на ленивую инициализацию переменных. До этого вы могли объявлять только инициализированную глобальную переменную, например «var x = foo;» если "foo" было константой времени компиляции. Без этого требования большинство программ можно писать без использования каких-либо константных объектов.
Итак, краткое резюме: конструкторы Const предназначены только для создания значений констант времени компиляции.
/ Л
[1] Или на самом деле: «Выражения с потенциальной константой времени компиляции», потому что это также может относиться к параметрам конструктора. [2] Итак, да, класс может одновременно иметь как константные, так и неконстантные конструкторы.