Late и отложенная инициализация

Последнее обновление: 04.04.2023

Нередко встречается ситуация, когда значения переменных становятся известны уже после их определения. Например:

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-константы

Слово 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-константе можно присвоить значение в любой части программы уже после создания объекта. Единственное ограничение - это можно сделать только один раз. При попытке повторого присваивания программы сгенерирует ошибку.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850