Sobrescrita de hashCode & ==
Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 71 de 110.
Por padrão, dois objetos em Dart são iguais apenas se forem a exata mesma instância na memória. Isso significa que dois objetos com valores idênticos ainda são considerados diferentes:
class Point {
int x, y;
Point(this.x, this.y);
}
void main() {
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // false - instâncias diferentes!
}Para comparar objetos por seus valores, você precisa sobrescrever tanto o operador == quanto o hashCode. Estes dois devem sempre ser sobrescritos juntos - se dois objetos são iguais, eles devem ter o mesmo código hash:
class Point {
final int x, y;
Point(this.x, this.y);
@override
bool operator ==(Object other) {
if (other is! Point) return false;
return x == other.x && y == other.y;
}
@override
int get hashCode => Object.hash(x, y);
}
void main() {
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // true - mesmos valores!
}O hashCode é crucial para coleções como chaves de Set e Map. O utilitário Object.hash() do Dart combina múltiplos valores em um único código hash. Sem um hashCode adequado, seus objetos não funcionarão corretamente em coleções baseadas em hash, mesmo que o == seja sobrescrito.
Desafio
FácilVamos construir um sistema de coordenadas onde os pontos podem ser comparados por seus valores em vez de seus endereços de memória! Você criará uma classe Coordinate que implementa adequadamente a igualdade baseada em valor, fazendo com que ela funcione corretamente com verificações de igualdade e coleções baseadas em hash, como o Set.
Você organizará seu código em dois arquivos:
coordinate.dart: Crie uma classeCoordinaterepresentando um ponto no espaço 2D com os camposfinal int xefinal int y. Sobrescreva tanto o operador==quanto ohashCodepara que duas coordenadas com os mesmos valores de x e y sejam consideradas iguais. UseObject.hash()para gerar o código hash a partir de ambos os campos.main.dart: Importe seu arquivo de coordenada e demonstre como a igualdade baseada em valor altera o comportamento de comparação de objetos:- Crie dois objetos
Coordinateseparados, ambos com x:5e y:10 - Imprima se eles são iguais usando
== - Imprima se eles são idênticos (mesma instância) usando
identical() - Crie um
Set<Coordinate>e adicione ambas as coordenadas a ele - Imprima o comprimento (length) do conjunto (deve ser 1, já que são iguais!)
- Crie uma terceira coordenada com x:
3e y:7, e adicione-a ao conjunto - Imprima o comprimento final do conjunto
- Crie dois objetos
Este desafio demonstra por que o hashCode e o == devem sempre ser sobrescritos juntos. O Set usa códigos hash para organizar os elementos, portanto, sem um hashCode adequado, mesmo objetos iguais podem ser armazenados como duplicatas!
Saída esperada:
true
false
1
2Folha de consulta
Por padrão, dois objetos em Dart são iguais apenas se forem a mesma instância na memória, não se tiverem os mesmos valores:
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // false - instâncias diferentes!Para comparar objetos por seus valores, sobrescreva tanto o operador == quanto o hashCode. Estes devem sempre ser sobrescritos juntos:
class Point {
final int x, y;
Point(this.x, this.y);
@override
bool operator ==(Object other) {
if (other is! Point) return false;
return x == other.x && y == other.y;
}
@override
int get hashCode => Object.hash(x, y);
}Use Object.hash() para combinar múltiplos valores em um único código hash. O hashCode é crucial para coleções como Set e Map - sem ele, objetos iguais não funcionarão corretamente em coleções baseadas em hash.
Para verificar se dois objetos são a mesma instância (não apenas valores iguais), use identical():
print(identical(p1, p2)); // false - instâncias diferentesExperimente você mesmo
import 'coordinate.dart';
void main() {
// TODO: Crie dois objetos Coordinate separados, ambos com x: 5 e y: 10
// TODO: Imprima se eles são iguais usando ==
// TODO: Imprima se eles são idênticos (mesma instância) usando identical()
// TODO: Crie um Set<Coordinate> e adicione ambas as coordenadas a ele
// TODO: Imprima o comprimento (length) do conjunto (deve ser 1, já que eles são iguais!)
// TODO: Crie uma terceira coordenada com x: 3 e y: 7, adicione-a ao conjunto
// TODO: Imprima o comprimento final do conjunto
}
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