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 é nulo." Ele converte um tipo anulável em não anulável, mas gera um erro se o valor for realmente nulo:
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 criar 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 represente um item do inventário. Alguns produtos têm informações completas, enquanto outros ainda estão sendo catalogados. Sua classe deve ter:- Uma
String namenão anulável — todo produto deve ter um nome - Um
double pricenão anulável — todo produto deve ter um preço - Uma
String? descriptionanulável — alguns produtos ainda não têm descrição - Uma
String? categoryanulável — alguns produtos ainda não foram categorizados - Um construtor que aceite
nameepricecomo parâmetros obrigatórios - Um método
getDescriptionLength()que retorne o tamanho da descrição usando?., ounullse não houver descrição - Um método
getCategoryUppercase()que retorne a categoria em letras maiúsculas usando!— esse método pressupõe que a categoria foi definida - Um método
displayProduct()que imprima as informações do produto, exibindoNo descriptionquando a descrição for nula eUncategorizedquando a categoria for nula
- Uma
main.dart: Importe sua classe de produto e demonstre ambos os operadores:- Crie um produto chamado
'Laptop'com preço999.99, depois defina sua descrição como'High-performance computing device'e sua categoria como'Electronics' - Crie um produto chamado
'Mystery Item'com preço49.99(deixe a descrição e a categoria como null) - 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 exibirá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: nullExperimente 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 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