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); // nullAo 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 é 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 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 classeReservationque 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 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 valor padrão de1— a estadia mínima é de uma noite - Uma
String? specialRequestsanulável — os hóspedes podem ter solicitações 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 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?., ounullse não estiver definido - Um método
printConfirmation()que exiba os detalhes da reserva
- Uma
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 quarto101por3noites, com a solicitação especial'Late checkout'e o 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 valor padrão de noites), definindo apenas a solicitação especial como'Extra pillows' - Crie uma reserva para o hóspede
'Emma Davis'no quarto310por2noites (deixe ambos os campos opcionais como null) - Para cada reserva, na ordem, chame
printConfirmation()e depois 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: nullExperimente 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 OOP
Arquivos externosBibliotecas e importsIntrodução à OOPClasses vs. objetosA palavra-chave thisMétodosVariáveis de instânciaNoções básicas de construtoresRevisão - Calculadora simples4Segurança contra nulos
Introdução à segurança contra nulosAnulável vs. não anulávelOs operadores ? e !Palavra-chave Late e segurança contra nulosOperadores conscientes de nulosSegurança contra nulos em classesRecapitulação — Sistema de perfil de usuário7Classes Abstratas e Interfaces
Classes AbstratasMétodos AbstratosInterfaces em DartInterfaces ImplícitasImplementação vs. ExtensãoMúltiplas InterfacesRecapitulação - Calculadora de Formas10Coleções e Genéricos
Visão geral de List, Set e MapColeções com segurança de tiposClasses genéricasMétodos genéricosRestrições genéricasIterable e IteratorRecapitulação - Armazenamento genérico13Conceitos Avançados de OOP
Composição vs. HerançaMétodos de ExtensãoClasses ChamáveisClasses Seladas (Dart 3)Registros (Dart 3)Padrões e Correspondência (3.0)Enums com Métodos16Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses de Livros e Usuários2Construtores em Dart
Construtor padrãoConstrutores nomeadosListas de inicializaçãoConstrutores constantesConstrutores factoryConstrutores de redirecionamentoRevisão - Construtor de formas5Encapsulamento
Membros Públicos vs. PrivadosConvenção do Prefixo _Privacidade no Nível da BibliotecaAprofundamento em Getters e SettersOcultação de InformaçõesRecapitulação - Registros de Alunos8Mixins
Introdução a MixinsCriando MixinsUsando vários MixinsPalavra-chave on em MixinsMixin vs HerançaMixin vs InterfaceRecapitulação - Sistema de Animais11Métodos Especiais
Sobrescrita de toString()Sobrescrita de hashCode e ==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 e ConstVariáveis LateMétodos e Campos EstáticosGetters e SettersRecapitulação - Gerenciador de Contas Bancárias6Herança
Herança BásicaA Palavra-chave superSobrescrita de MétodosA Anotação @overrideA Palavra-chave final para ClassesConstrutores e HerançaRecapitulação - Hierarquia de Funcionários9Polimorfismo
Fundamentos do polimorfismoPolimorfismo por meio de interfacesVerificação de tipos em tempo de execuçãoOs operadores is e asPalavra-chave covarianteRecapitulação — Processador de pagamentos12OOP Assíncrona
Futures e async/awaitNoções básicas de StreamsControladores de StreamConstrutores assíncronosAsync em métodos de classeRecapitulação — Buscador de dados15Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão RepositoryPratique por conta própria: Compilador de Dart online