Нередко встречается ситуация, когда значения переменных становятся известны уже после их определения. Например:
class Person{ String name = ""; int age = 0; Person(String name, int age) { // проверяем переданные значения if(name != "admin") this.name = name; else this.name = "Undefined"; if(age > 0 && age < 111) this.age = age; else this.age = 18; } void display() => print("Name: $name \tAge: $age"); }
Здесь значения полей name и age устанавливаются в конструкторе в зависимости от условия. Однако по умолчанию все переменные, которые не доступают null, должны иметь некоторое значение. Соответственно полям name и age устанавливаем базовые значения:
String name = ""; int age = 0;
В качестве альтернативы мы могли бы определить эти переменные как допускающие null
String? name; int? age;
Однако это логически не совсем правильно, поскольку ни имя, ни возраст пользователя у нас не должны в принципе быть равны null. Они всегда должны иметь какие-то конкретные значения. И язык Dart для этой ситуации предоставляет решение - мы можем определить подобные переменные с ключевым словом late:
class Person{ late String name; // отложенная инициализация late int age; Person(String name, int age) { if(name != "admin") this.name = name; else this.name = "Undefined"; if(age > 0 && age < 111) this.age = age; else this.age = 18; } void display() => print("Name: $name \tAge: $age"); } void main (){ Person tom = Person("Tom", 38); tom.display(); }
Ключевое слово late указывает, что переменная будет инициализирована впоследствии, то есть это так называемая отложенная инициализация. Причем установить значения можно в любой части программы, а не только в конструкторе. Например, вынесем установку поля age в отдельный метод:
class Person{ String name; late int age; // отложенная инициализация Person(this.name); void setAge(int age){ if(age > 0 && age < 111) this.age = age; else this.age = 18; } void display() => print("Name: $name \tAge: $age"); } void main (){ Person tom = Person("Tom"); tom.setAge(38); tom.display(); }
В то же время отложенная инициализация переменных может быть чревата, если мы вдруг попытаемся получить их значение до инициализации:
Person tom = Person("Tom"); tom.display(); // ! Ошибка - age не инициализирована.
late также имеет преимущество в том, что позволяет оптимизировать производительность. Например, возьмем следующий код:
class Person{ String name; late int age = getDefaultAge(); Person(this.name); void display() => print("Name: $name \tAge: $age"); } // функция, которая возвращает значение по умолчанию int getDefaultAge() => 18; void main (){ Person tom = Person("Tom"); print(tom.name); // Tom }
Здесь значение переменной age инициализируется с помощью внешней функции getDefaultAge, которая возвращает некоторое значение. Но при выполнении этой программы переменная age в реальности
так и не будет инициализирована. Почему? Потому в методе main ни прямым, ни косвенным образом нет обращения к переменной age, она не нужна. Поэтому благодаря применению оператора
late
она не будет инициализирована. Late-переменные инициализируются только при первом обращении к ним. Таким образом, применение late
может быть полезно, если процесс инициализации занимает много ресурсов, а сама переменная может так и не потребоваться в процессе выполнения программы.
Слово late также можно комбинировать с final для определения констант, которые инициализируются в отложенном режиме. Например:
class Person{ final String name; late final int age; Person(this.name); void setAge(int age){ if(age > 0 && age < 111) this.age = age; else this.age = 18; } void display() => print("Name: $name \tAge: $age"); } void main (){ Person tom = Person("Tom"); print(tom.name); // Tom tom.setAge(38); // инициализируем константу age tom.display(); // Name: Tom Age: 38 }
В отличие от обычных final-констант константы с отложенной инициализацией не нужно инициализировать при объявлении или с помощью списка инициализации конструктора. late-константе можно присвоить значение в любой части программы уже после создания объекта. Единственное ограничение - это можно сделать только один раз. При попытке повторого присваивания программы сгенерирует ошибку.