Os Operadores ? e !
Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 25 de 110.
Ao trabalhar com tipos anuláveis, o Dart fornece dois operadores essenciais para acessar valores com segurança. O operador de acesso condicional (null-aware) ?. e o operador de asserção nula (null assertion) ! oferecem controle sobre como você lida com valores potencialmente nulos.
O operador ?. acessa com segurança propriedades ou métodos em um valor anulável. Se o valor for null, toda a expressão retornará null em vez de causar um erro:
String? name = 'Alice';
print(name?.length); // 5
name = null;
print(name?.length); // null (sem erro!)O operador ! informa ao Dart: "Tenho certeza de que este valor não é null". Ele converte um tipo nullable em non-nullable, mas lança um erro se o valor for realmente null:
String? maybeName = 'Bob';
String definite = maybeName!; // OK - afirmamos que não é nulo
print(definite.length); // 3
String? empty = null;
// String crash = empty!; // Erro de tempo de execução! O valor era nuloUse ?. quando você quiser lidar graciosamente com valores nulos. Use ! apenas quando tiver certeza absoluta de que um valor existe - talvez após verificá-lo com uma instrução if ou quando souber que a lógica garante um valor. O uso excessivo de ! anula o propósito da segurança de nulos (null safety) e pode levar a falhas em tempo de execução.
Desafio
FácilVamos construir um sistema de inventário de produtos que demonstra como trabalhar com segurança com valores anuláveis usando os operadores ?. e !.
Você criará dois arquivos para organizar seu código:
product.dart: Defina uma classeProductque representa um item no inventário. Alguns produtos possuem informações completas, enquanto outros ainda estão sendo catalogados. Sua classe deve ter:- Um
String namenão anulável - todo produto deve ter um nome - Um
double pricenão anulável - todo produto deve ter um preço - Um
String? descriptionanulável - alguns produtos ainda não possuem descrições - Um
String? categoryanulável - alguns produtos ainda não foram categorizados - Um construtor que recebe
nameepricecomo parâmetros obrigatórios - Um método
getDescriptionLength()que retorna o comprimento da descrição usando?., ounullse não houver descrição - Um método
getCategoryUppercase()que retorna a categoria em letras maiúsculas usando!- este método assume que a categoria foi definida - Um método
displayProduct()que imprime as informações do produto, exibindo"No description"quando a descrição for nula e"Uncategorized"quando a categoria for nula
- Um
main.dart: Importe sua classe de produto e demonstre ambos os operadores:- Crie um produto chamado
'Laptop'com o preço999.99, depois defina sua descrição como'High-performance computing device'e a categoria como'Electronics' - Crie um produto chamado
'Mystery Item'com o preço49.99(deixe a descrição e a categoria como nulas) - Para o Laptop: chame
displayProduct(), imprimaDescription length: [length]usandogetDescriptionLength(), e imprimaCategory: [uppercase]usandogetCategoryUppercase() - Para o Mystery Item: chame
displayProduct()e imprimaDescription length: [length]usandogetDescriptionLength()(isso mostraránull)
- Crie um produto chamado
O método displayProduct() deve imprimir neste formato:
Product: [name] - $[price]
Description: [description or "No description"]
Category: [category or "Uncategorized"]Saída esperada:
Product: Laptop - $999.99
Description: High-performance computing device
Category: Electronics
Description length: 32
Category: ELECTRONICS
Product: Mystery Item - $49.99
Description: No description
Category: Uncategorized
Description length: nullFolha de consulta
O operador de acesso null-aware ?. acessa com segurança propriedades ou métodos em valores anuláveis (nullable). Se o valor for nulo, ele retorna nulo em vez de lançar um erro:
String? name = 'Alice';
print(name?.length); // 5
name = null;
print(name?.length); // null (sem travamentos!)O operador de asserção nula ! converte um tipo anulável para não anulável. Use-o apenas quando tiver certeza de que o valor não é nulo, pois ele lançará um erro de tempo de execução se o valor for realmente nulo:
String? maybeName = 'Bob';
String definite = maybeName!; // OK - afirma que não é nulo
print(definite.length); // 3
String? empty = null;
// String crash = empty!; // Erro de tempo de execução!Use ?. para um tratamento de nulos gracioso e ! apenas quando tiver certeza absoluta de que um valor existe.
Experimente você mesmo
import 'product.dart';
void main() {
// TODO: Crie um produto chamado 'Laptop' com o preço 999.99
// Depois, defina sua descrição como 'High-performance computing device'
// E defina sua categoria como 'Electronics'
// TODO: Crie um produto chamado 'Mystery Item' com o preço 49.99
// Deixe a descrição e a categoria como null
// TODO: Para o Laptop:
// - Chame displayProduct()
// - Imprima "Description length: [length]" usando getDescriptionLength()
// - Imprima "Category: [uppercase]" usando getCategoryUppercase()
// TODO: Para o Mystery Item:
// - Chame displayProduct()
// - Imprima "Description length: [length]" usando getDescriptionLength()
}
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