Null Safety 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 de nulo (null safety) afeta 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); // nullAo acessar campos anuláveis, use os operadores de reconhecimento de nulo (null-aware) 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 é opcionalAo 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.
Desafio
FácilVamos construir um sistema de reservas para um hotel que demonstra como projetar classes com uma mistura cuidadosa de campos obrigatórios e opcionais usando null safety.
Você organizará seu código em dois arquivos:
reservation.dart: Defina uma classeReservationque representa uma reserva de hotel. Toda reserva precisa de certas informações essenciais, mas alguns detalhes são extras opcionais que os hóspedes podem ou não fornecer. Sua classe deve ter:- Um
String guestNamenão anulável - toda reserva deve ter o nome de um hóspede - Um
int roomNumbernão anulável - toda reserva deve ter um quarto atribuído - Um
int nightsnão anulável com um valor padrão de1- a estadia mínima é de uma noite - Um
String? specialRequestsanulável - os hóspedes podem ter pedidos especiais - Um
DateTime? checkInTimeanulável - o horário exato de chegada pode não ser conhecido - Um construtor usando parâmetros nomeados com
requiredparaguestNameeroomNumber, enquantonightsdeve ser opcional com seu valor padrão - Um método
getRequestsSummary()que retorna os pedidos especiais se fornecidos, ou'No special requests'usando o operador?? - Um método
getArrivalInfo()que retorna a representação em string do horário de check-in usando?., ounullse não estiver definido - Um método
printConfirmation()que exibe os detalhes da reserva
- Um
main.dart: Importe sua classe de reserva e crie reservas que mostrem diferentes combinações de dados obrigatórios e opcionais:- Crie uma reserva para o hóspede
'Sarah Johnson'no quarto101por3noites, com pedidos especiais'Late checkout'e horário de check-in definido comoDateTime(2024, 6, 15, 14, 30) - Crie uma reserva para o hóspede
'Mike Chen'no quarto205(use o padrão de noites), com apenas pedidos especiais definidos como'Extra pillows' - Crie uma reserva para a hóspede
'Emma Davis'no quarto310por2noites (deixe ambos os campos opcionais como nulos) - Para cada reserva em ordem, chame
printConfirmation(), e então imprimaArrival: [result]usandogetArrivalInfo()
- Crie uma reserva para o hóspede
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: nullFolha de consulta
Campos não anuláveis devem ser inicializados antes da execução do corpo do construtor, enquanto campos anuláveis podem permanecer não inicializados (o padrão é null):
class User {
String name; // Deve ser inicializado
String? bio; // Opcional - o padrão é null
int age = 0; // Possui valor padrão
User(this.name);
}Use campos anuláveis para dados opcionais:
class Product {
String title;
String? description; // Opcional
double? discount; // Opcional
Product(this.title);
}
var item = Product('Laptop');
print(item.description); // nullAcesse campos anuláveis com operadores null-aware:
class Order {
String id;
User? customer;
Order(this.id);
String getCustomerName() {
return customer?.name ?? 'Guest';
}
}Use a palavra-chave required em parâmetros nomeados para valores essenciais:
class Account {
String email;
String? phone;
Account({required this.email, this.phone});
}
var acc = Account(email: 'test@mail.com');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()
}
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
1Fundamentos de POO
Arquivos ExternosBibliotecas e ImportaçõesIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosVariáveis de InstânciaFundamentos de ConstrutoresRecapitulação - Calculadora Simples4Null Safety
Introdução ao Null SafetyNullable vs Non-NullableOs Operadores ? e !Palavra-chave Late e Null SafetyOperadores Null-AwareNull Safety em ClassesRecapitulação - Sistema de Perfil de Usuário7Classes Abstratas e Interfaces
Classes AbstratasMétodos AbstratosInterfaces em DartInterfaces ImplícitasImplementando vs EstendendoMúltiplas InterfacesRecapitulação - Calculadora de Formas10Coleções e Generics
Visão Geral de List, Set e MapColeções Type-SafeClasses GenéricasMétodos GenéricosRestrições de GenericsIterable e IteratorRecapitulação - Armazenamento Genérico13Conceitos Avançados de POO
Composição vs HerançaMétodos de ExtensãoClasses ChamáveisClasses Seladas (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums com Métodos16Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário2Construtores em Dart
Construtor PadrãoConstrutores NomeadosListas de InicializaçãoConstrutores ConstantesConstrutores FactoryConstrutores de RedirecionamentoRecapitulação - Shape Builder5Encapsulamento
Membros Públicos vs PrivadosA Convenção do Prefixo _Privacidade em Nível de BibliotecaAprofundamento em Getters & SettersOcultação de InformaçãoRecapitulação - Registros de Estudantes8Mixins
Introdução aos MixinsCriando MixinsUsando Múltiplos MixinsPalavra-chave on em MixinsMixin vs HerançaMixin vs InterfaceRecapitulação - Sistema Animal11Métodos Especiais
Sobrescrita de toString()Sobrescrita de hashCode & ==Interface ComparableMétodo call()Sobrescrita de noSuchMethodRecapitulação - Coleção Personalizada14Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy3Propriedades de Classe
Membros de Instância vs EstáticosCampos Final & ConstVariáveis LateMétodos e Campos EstáticosGetters e SettersRecapitulação - Gerenciador de Conta Bancária6Herança
Herança BásicaA Palavra-chave superSobrescrita de MétodoA Anotação @overrideA Palavra-chave final para ClassesConstrutores e HerançaRecapitulação - Hierarquia de Funcionários9Polimorfismo
Fundamentos do PolimorfismoPolimorfismo via InterfacesVerificação de Tipos em Tempo de ExecuçãoOs Operadores is & asPalavra-chave CovariantRecapitulação - Processador de Pagamentos12POO Assíncrona
Futures e async/awaitFundamentos de StreamsStream ControllersConstrutores AssíncronosAsync em Métodos de ClasseRecapitulação - Data Fetcher15Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Repository