Nullable vs Non-Nullable
Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 24 de 110.
Agora que você entende por que a null safety é importante, vamos ver como declarar variáveis que podem conter null. No Dart, você torna um tipo anulável adicionando um ? após o nome do tipo.
String name = 'Alice'; // Não anulável - deve sempre ter um valor
String? nickname; // Anulável - pode ser nulo ou uma String
print(name); // Alice
print(nickname); // nullA principal diferença é que os tipos não anuláveis garantem que um valor existe, enquanto os tipos anuláveis permitem explicitamente a ausência de um valor. Essa distinção se aplica a todos os tipos:
int count = 10; // Deve ter um valor
int? optionalCount; // Pode ser nulo
List<String> items = []; // Deve ser uma lista (pode estar vazia)
List<String>? maybeItems; // Pode ser nulo (nenhuma lista)Observe que uma lista vazia [] é diferente de null. Uma lista vazia existe, mas não contém nada; null significa que nenhuma lista existe.
O compilador impõe essas regras estritamente. Você não pode atribuir null a uma variável não anulável, e você não pode usar uma variável anulável onde uma não anulável é esperada sem primeiro verificar se há nulo:
String? maybeName = 'Bob';
// String definite = maybeName; // Erro! Não é possível atribuir um valor anulável a um não anulávelNa próxima lição, você aprenderá os operadores que o Dart oferece para trabalhar com segurança com valores anuláveis.
Desafio
FácilVamos construir um sistema de lista de filmes que demonstra a diferença entre informações obrigatórias e opcionais usando tipos anuláveis (nullable) e não anuláveis (non-nullable).
Você criará dois arquivos para organizar seu código:
movie.dart: Defina uma classeMovieque representa um filme em sua lista. Todo filme deve ter um título e um ano de lançamento (estes são sempre obrigatórios), mas alguns detalhes adicionais podem não estar disponíveis para todos os filmes. Sua classe deve ter:- Um
String titlenão anulável - todo filme deve ter um título - Um
int yearnão anulável - todo filme deve ter um ano de lançamento - Um
String? directoranulável - o diretor pode ser desconhecido - Um
double? ratinganulável - nem todos os filmes foram avaliados ainda - Um construtor que recebe
titleeyearcomo parâmetros obrigatórios - Um método
displayInfo()que imprime as informações do filme, exibindo"Unknown"para diretor ausente e"Not rated"para avaliação ausente
- Um
main.dart: Importe sua classe de filme e crie uma pequena lista demonstrando dados de filmes completos e incompletos:- Crie um filme com o título
'Inception'e ano2010, depois defina seu diretor como'Christopher Nolan'e a avaliação como8.8 - Crie um filme com o título
'The Matrix'e ano1999, depois defina apenas sua avaliação como8.7(deixe o diretor como nulo) - Crie um filme com o título
'Upcoming Film'e ano2025(deixe tanto o diretor quanto a avaliação como nulos) - Chame
displayInfo()em cada filme na ordem listada acima
- Crie um filme com o título
O método displayInfo() deve imprimir exatamente neste formato:
[title] ([year])
Director: [director or "Unknown"]
Rating: [rating or "Not rated"]Observe como os campos não anuláveis (title e year) têm garantia de sempre possuir valores, enquanto os campos anuláveis (director? e rating?) podem permanecer sem definição. Isso reflete cenários do mundo real onde alguns dados são essenciais e outros são opcionais.
Saída esperada:
Inception (2010)
Director: Christopher Nolan
Rating: 8.8
The Matrix (1999)
Director: Unknown
Rating: 8.7
Upcoming Film (2025)
Director: Unknown
Rating: Not ratedFolha de consulta
Para tornar um tipo anulável em Dart, adicione um ? após o nome do tipo:
String name = 'Alice'; // Não nulo - deve sempre ter um valor
String? nickname; // Anulável - pode ser nulo ou uma StringEssa distinção se aplica a todos os tipos:
int count = 10; // Deve ter um valor
int? optionalCount; // Pode ser nulo
List<String> items = []; // Deve ser uma lista (pode estar vazia)
List<String>? maybeItems; // Pode ser nulo (nenhuma lista de fato)Uma lista vazia [] é diferente de null. Uma lista vazia existe, mas não contém nada; null significa que nenhuma lista existe.
O compilador impõe as regras de null safety rigorosamente. Você não pode atribuir null a uma variável não nula, e não pode usar uma variável anulável onde uma não nula é esperada sem antes verificar se ela é nula:
String? maybeName = 'Bob';
// String definite = maybeName; // Erro! Não é possível atribuir um valor anulável a um não nuloExperimente você mesmo
import 'movie.dart';
void main() {
// TODO: Criar um filme com o título 'Inception' e ano 2010
// Depois definir seu diretor como 'Christopher Nolan' e a nota como 8.8
// TODO: Criar um filme com o título 'The Matrix' e ano 1999
// Depois definir apenas sua nota como 8.7 (deixar o diretor como nulo)
// TODO: Criar um filme com o título 'Upcoming Film' e ano 2025
// Deixar tanto o diretor quanto a nota como nulos
// TODO: Chamar displayInfo() em cada filme na ordem
}
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