Я вважаю відповідь Лассе в блозі Кріса Стормса чудовим поясненням.
Сподіваюся, вони не заперечують, що я копіюю вміст.
Це чітке пояснення підсумкових полів, але це насправді не пояснює конструкторів const. Ніщо в цих прикладах насправді не використовує те, що конструктори є конструкторами const. Будь-який клас може мати остаточні поля, конструктори const чи ні.
Поле в Dart - це дійсно анонімне місце зберігання в поєднанні з автоматично створеним геттером і сеттером, який читає та оновлює сховище, а також його можна ініціалізувати у списку ініціалізатора конструктора.
Заключне поле те саме, лише без сеттера, тому єдиний спосіб встановити його значення знаходиться в списку ініціалізатора конструктора, і немає можливості змінити значення після цього - отже, "остаточне".
Сенс конструкторів const не в ініціалізації кінцевих полів, будь-який генеративний конструктор може це зробити. Сенс полягає у створенні постійних значень часу компіляції: Об'єкти, де всі значення поля відомі вже під час компіляції, не виконуючи жодних операторів.
Це ставить деякі обмеження для класу та конструктора. Конструктор const не може мати тіло (жодних операцій не виконується!), А його клас не повинен мати жодних не остаточних полів (значення, яке ми "знаємо" під час компіляції, не може бути здатне змінювати пізніше). Список ініціалізатора повинен також ініціалізувати поля лише для інших констант часу компіляції, тому права частина обмежується "постійними виразами часу компіляції" [1]. І це повинно бути префіксом "const" - інакше ви просто отримаєте звичайний конструктор, який відповідає цим вимогам. Це абсолютно добре, це просто не конструктор const.
Щоб використовувати конструктор const для фактичного створення константного об'єкта часу компіляції, ви замінюєте "new" на "const" на "new" -вираз. Ви все ще можете використовувати "new" з const-конструктором, і він все одно створить об'єкт, але це буде просто нормальний новий об'єкт, а не постійне значення часу компіляції. Тобто: Конструктор 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)", ви створюєте лише один об'єкт. Це може бути корисно, але не настільки, як здавалося б, оскільки ви можете просто зробити змінну const, щоб утримувати значення і використовувати замість нього змінну.
Отже, для чого потрібні постійні час компіляції?
- Вони корисні для перерахунків.
- Ви можете використовувати постійні значення часу компіляції у випадках комутації.
- Вони використовуються як примітки.
Константи часу компіляції раніше були важливішими, перш ніж Dart перейшов на ліниво ініціалізацію змінних. Перед цим ви могли лише оголосити ініціалізовану глобальну змінну на зразок "var x = foo;" якщо "foo" була константа часу компіляції. Без цієї вимоги більшість програм можна писати без використання об'єктів const
Отже, короткий підсумок: Конструктори Const призначені лише для створення постійних значень часу компіляції.
/ Л
[1] Або дійсно: "Потенційно виражені постійні вирази часу", оскільки вони також можуть посилатися на параметри конструктора. [2] Отже, так, клас може мати одночасно конструктори const і non-const.