Menu
Coddy logo textTech

Segurança contra nulos em classes

Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy. Lição 28 de 110.

Ao projetar classes, você precisa decidir quais campos são obrigatórios e quais são opcionais. A segurança contra nulos afeta a forma como você declara variáveis de instância e escreve construtores.

Campos não anuláveis devem ser inicializados antes que o corpo do construtor seja executado. Você pode fazer isso por meio de listas de inicialização, parâmetros do construtor ou valores padrão:

class User {
  String name;           // Deve ser inicializado
  String? bio;           // Opcional - pode ser nulo
  int age = 0;           // Possui valor padrão
  
  User(this.name);       // name é inicializado via parâmetro
}

Para campos anuláveis, você pode deixá-los não inicializados, já que o padrão deles é null. Isso é útil para dados opcionais:

class Product {
  String title;
  String? description;   // Descrição opcional do produto
  double? discount;      // Sem desconto por padrão
  
  Product(this.title);
}

var item = Product('Laptop');
print(item.description);  // null
print(item.discount);     // null

Ao acessar campos que podem ser nulos, use os operadores de acesso seguro para nulos que você aprendeu:

class Order {
  String id;
  User? customer;
  
  Order(this.id);
  
  String getCustomerName() {
    return customer?.name ?? 'Guest';
  }
}

A palavra-chave required em parâmetros nomeados garante que os chamadores forneçam valores essenciais:

class Account {
  String email;
  String? phone;
  
  Account({required this.email, this.phone});
}

var acc = Account(email: 'test@mail.com');  // phone é opcional

Ao escolher criteriosamente entre campos anuláveis e não anuláveis, você cria classes que comunicam claramente quais dados são essenciais e quais são opcionais.

challenge icon

Desafio

Fácil

Vamos criar um sistema de reservas para um hotel que demonstra como projetar classes com uma combinação cuidadosa de campos obrigatórios e opcionais usando segurança contra nulos.

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

  • reservation.dart: Defina uma classe Reservation que represente uma reserva de hotel. Toda reserva precisa de algumas informações essenciais, mas alguns detalhes são extras opcionais que os hóspedes podem ou não fornecer. Sua classe deve ter:
    • Uma String guestName não anulável — toda reserva deve ter o nome de um hóspede
    • Um int roomNumber não anulável — toda reserva deve ter um quarto atribuído
    • Um int nights não anulável com valor padrão de 1 — a estadia mínima é de uma noite
    • Uma String? specialRequests anulável — os hóspedes podem ter solicitações especiais
    • Um DateTime? checkInTime anulável — o horário exato de chegada pode não ser conhecido
    • Um construtor usando parâmetros nomeados com required para guestName e roomNumber, enquanto nights deve ser opcional com seu valor padrão
    • Um método getRequestsSummary() que retorne as solicitações especiais, caso fornecidas, ou 'No special requests' usando o operador ??
    • Um método getArrivalInfo() que retorne a representação em string do horário de check-in usando ?., ou null se não estiver definido
    • Um método printConfirmation() que exiba os detalhes da reserva
  • main.dart: Importe sua classe de reserva e crie reservas que demonstrem diferentes combinações de dados obrigatórios e opcionais:
    • Crie uma reserva para o hóspede 'Sarah Johnson' no quarto 101 por 3 noites, com a solicitação especial 'Late checkout' e o horário de check-in definido como DateTime(2024, 6, 15, 14, 30)
    • Crie uma reserva para o hóspede 'Mike Chen' no quarto 205 (use o valor padrão de noites), definindo apenas a solicitação especial como 'Extra pillows'
    • Crie uma reserva para o hóspede 'Emma Davis' no quarto 310 por 2 noites (deixe ambos os campos opcionais como null)
    • Para cada reserva, na ordem, chame printConfirmation() e depois imprima Arrival: [result] usando getArrivalInfo()

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

=== Reservation Confirmed ===
Guest: [guestName]
Room: [roomNumber]
Nights: [nights]
Requests: [getRequestsSummary()]

Saída esperada:

=== Reservation Confirmed ===
Guest: Sarah Johnson
Room: 101
Nights: 3
Requests: Late checkout
Arrival: 2024-06-15 14:30:00.000
=== Reservation Confirmed ===
Guest: Mike Chen
Room: 205
Nights: 1
Requests: Extra pillows
Arrival: null
=== Reservation Confirmed ===
Guest: Emma Davis
Room: 310
Nights: 2
Requests: No special requests
Arrival: null

Experimente você mesmo

import 'reservation.dart';

void main() {
  // TODO: Criar reserva para 'Sarah Johnson'
  // Quarto 101, 3 noites, pedidos especiais: 'Late checkout'
  // Horário de check-in: DateTime(2024, 6, 15, 14, 30)
  
  // TODO: Criar reserva para 'Mike Chen'
  // Quarto 205, usar noites padrão, pedidos especiais: 'Extra pillows'
  // Nenhum horário de check-in especificado
  
  // TODO: Criar reserva para 'Emma Davis'
  // Quarto 310, 2 noites, sem pedidos especiais, sem horário de check-in
  
  // TODO: Para cada reserva em ordem:
  // 1. Chamar printConfirmation()
  // 2. Imprimir 'Arrival: [result]' usando getArrivalInfo()
}
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

Pratique por conta própria: Compilador de Dart online