Menu
Coddy logo textTech

Palavra-chave Late e Null Safety

Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 26 de 110.

Às vezes você precisa de uma variável não anulável, mas não pode atribuir seu valor imediatamente. A palavra-chave late diz ao Dart "Eu prometo inicializar isso antes de usar."

late String description;

void setup() {
  description = 'Initialized later';
}

void main() {
  setup();
  print(description);  // Funciona - inicializado antes do uso
}

Sem late, o Dart reclamaria que description não está inicializada. Com late, você assume a responsabilidade de garantir que ela tenha um valor antes do acesso. Se você tentar lê-la antes da inicialização, receberá um erro de tempo de execução.

Um caso de uso comum são computações caras que você deseja atrasar até que sejam realmente necessárias:

class DataProcessor {
  late List<int> data = _loadData();
  
  List<int> _loadData() {
    print('Loading data...');
    return [1, 2, 3, 4, 5];
  }
}

DataProcessor processor = DataProcessor();
// 'Loading data...' ainda não foi impresso
print(processor.data);  // Agora ele carrega e imprime

O método _loadData() só é executado quando data é acessado pela primeira vez, não quando o objeto é criado. Esta inicialização preguiçosa pode melhorar o desempenho quando o valor pode nunca ser necessário.

Use late quando você tiver certeza de que uma variável será inicializada antes do uso, mas não puder fazer isso na declaração. É uma promessa ao compilador — quebre essa promessa e seu programa travará.

challenge icon

Desafio

Fácil

Vamos construir um gerenciador de configurações que demonstra o poder das variáveis late para inicialização adiada. Você criará um sistema onde operações de configuração custosas só acontecem quando a configuração é realmente necessária.

Você organizará seu código em dois arquivos:

  • config_manager.dart: Defina uma classe ConfigManager que lida com as configurações da aplicação. A classe deve ter:
    • Uma String appName passada através do construtor
    • Uma late String databaseUrl que será inicializada posteriormente através de um método de configuração
    • Um late int maxConnections com inicialização preguiçosa (lazy) que imprime Calculating max connections... quando acessado pela primeira vez e retorna 100
    • Um método setupDatabase(String url) que inicializa a databaseUrl
    • Um método displayConfig() que imprime a configuração atual
  • main.dart: Importe seu gerenciador de configurações e demonstre como as variáveis late funcionam:
    • Crie um ConfigManager com o nome do app 'MyApp'
    • Imprima Config created
    • Chame setupDatabase com a URL 'localhost:5432/mydb'
    • Imprima Accessing max connections...
    • Imprima o valor de maxConnections
    • Imprima Accessing max connections again...
    • Imprima o valor de maxConnections novamente (observe que a mensagem de cálculo não se repete)
    • Chame displayConfig()

O método displayConfig() deve imprimir neste formato:

--- Configuration ---
App: [appName]
Database: [databaseUrl]
Max Connections: [maxConnections]

Observe como o cálculo preguiçoso de maxConnections só executa uma vez - a primeira vez que você o acessa. Este é o benefício do late com um inicializador!

Saída esperada:

Config created
Accessing max connections...
Calculating max connections...
100
Accessing max connections again...
100
--- Configuration ---
App: MyApp
Database: localhost:5432/mydb
Max Connections: 100

Folha de consulta

A palavra-chave late permite declarar variáveis não anuláveis que serão inicializadas posteriormente, antes de serem utilizadas:

late String description;

void setup() {
  description = 'Initialized later';
}

void main() {
  setup();
  print(description);  // Works - initialized before use
}

Sem o late, o Dart exige a inicialização imediata. Com o late, você promete inicializar a variável antes de acessá-la. Ler uma variável late não inicializada causa um erro em tempo de execução.

Inicialização Preguiçosa (Lazy Initialization): Quando uma variável late possui um inicializador, a inicialização é adiada até que a variável seja acessada pela primeira vez:

class DataProcessor {
  late List<int> data = _loadData();
  
  List<int> _loadData() {
    print('Loading data...');
    return [1, 2, 3, 4, 5];
  }
}

DataProcessor processor = DataProcessor();
// 'Loading data...' hasn't printed yet
print(processor.data);  // Now it loads and prints

O método _loadData() só é executado quando data é acessado pela primeira vez, não quando o objeto é criado. Isso melhora o desempenho quando o valor pode nunca ser necessário ou quando a inicialização é dispendiosa.

Experimente você mesmo

import 'config_manager.dart';

void main() {
  // TODO: Crie um ConfigManager com o nome do app 'MyApp'
  
  // TODO: Imprima 'Config created'
  
  // TODO: Chame setupDatabase com a URL 'localhost:5432/mydb'
  
  // TODO: Imprima 'Accessing max connections...'
  
  // TODO: Imprima o valor de maxConnections
  
  // TODO: Imprima 'Accessing max connections again...'
  
  // TODO: Imprima o valor de maxConnections novamente
  // (observe que a mensagem de cálculo não se repete)
  
  // TODO: Chame displayConfig()
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos